AzuraJS Logo
AzuraJSFramework
v2.2 Beta

Validation

Validate request data with schemas and DTOs

Validation ✅

AzuraJS provides built-in validation for request data using schemas and DTOs (Data Transfer Objects).

Schema Validation 📋

Validate request bodies against TypeScript-like schemas.

Basic Schema

import { Post, Body, Res } from "azurajs/decorators";
import { validateSchema } from "azurajs/validators";
import type { ResponseServer } from "azurajs";

const userSchema = {
  name: "string",
  email: "string",
  age: "number",
};

@Post()
createUser(@Body() body: any, @Res() res: ResponseServer) {
  try {
    validateSchema(userSchema, body);
    // Body is valid
    res.status(201).json({ user: body });
  } catch (error: any) {
    res.status(400).json({ error: error.message });
  }
}

Nested Schemas

const addressSchema = {
  street: "string",
  city: "string",
  zipCode: "string",
};

const userSchema = {
  name: "string",
  email: "string",
  address: addressSchema,  // Nested object
};

validateSchema(userSchema, body);

Array Schemas

const userSchema = {
  name: "string",
  tags: ["string"],  // Array of strings
  skills: [
    {
      name: "string",
      level: "number",
    }
  ],  // Array of objects
};

DTO Validation 🎯

Use the @validateDto decorator for class-based validation.

Creating a DTO

class CreateUserDto {
  name: string;
  email: string;
  password: string;
  age?: number;

  constructor(data: any) {
    if (!data.name || typeof data.name !== "string") {
      throw new Error("Name is required and must be a string");
    }
    if (!data.email || typeof data.email !== "string") {
      throw new Error("Email is required and must be a string");
    }
    if (!data.password || typeof data.password !== "string") {
      throw new Error("Password is required and must be a string");
    }

    this.name = data.name;
    this.email = data.email;
    this.password = data.password;
    
    if (data.age !== undefined) {
      if (typeof data.age !== "number") {
        throw new Error("Age must be a number");
      }
      this.age = data.age;
    }
  }
}

Using DTO in Controllers

import { Controller, Post, Res } from "azurajs/decorators";
import { validateDto } from "azurajs/validators";
import type { ResponseServer } from "azurajs";

@Controller("/api/users")
export class UserController {
  @Post()
  createUser(
    @validateDto(CreateUserDto) data: CreateUserDto,
    @Res() res: ResponseServer
  ) {
    // data is validated and typed
    res.status(201).json({ user: data });
  }
}

Custom Validators 🛠️

Create reusable validation functions:

// validators/email.ts
export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

// validators/password.ts
export function isStrongPassword(password: string): boolean {
  return password.length >= 8 && 
         /[A-Z]/.test(password) &&
         /[a-z]/.test(password) &&
         /[0-9]/.test(password);
}

// Use in DTOs
class CreateUserDto {
  name: string;
  email: string;
  password: string;

  constructor(data: any) {
    // Validate name
    if (!data.name || typeof data.name !== "string" || data.name.length < 2) {
      throw new Error("Name must be at least 2 characters");
    }

    // Validate email
    if (!data.email || !isValidEmail(data.email)) {
      throw new Error("Invalid email format");
    }

    // Validate password
    if (!data.password || !isStrongPassword(data.password)) {
      throw new Error("Password must be at least 8 characters with uppercase, lowercase, and numbers");
    }

    this.name = data.name;
    this.email = data.email;
    this.password = data.password;
  }
}

Validation with Zod 📦

For more advanced validation, use Zod:

bun add zod
import { z } from "zod";

// Define schema
const createUserSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  password: z.string().min(8),
  age: z.number().int().positive().optional(),
  role: z.enum(["user", "admin"]).default("user"),
});

type CreateUserDto = z.infer<typeof createUserSchema>;

// Use in controller
@Post()
createUser(@Body() body: any, @Res() res: ResponseServer) {
  try {
    const data = createUserSchema.parse(body);
    // data is validated and typed
    res.status(201).json({ user: data });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({ 
        error: "Validation failed", 
        details: error.errors 
      });
    }
  }
}

Query Parameter Validation 🔍

Validate query parameters:

const searchQuerySchema = z.object({
  q: z.string().min(1),
  limit: z.string().regex(/^\d+$/).transform(Number).default("10"),
  offset: z.string().regex(/^\d+$/).transform(Number).default("0"),
});

@Get("/search")
search(@Query() query: any, @Res() res: ResponseServer) {
  try {
    const params = searchQuerySchema.parse(query);
    // Search with validated params
    res.json({ 
      query: params.q, 
      limit: params.limit, 
      offset: params.offset 
    });
  } catch (error) {
    res.status(400).json({ error: "Invalid query parameters" });
  }
}

Path Parameter Validation 🛣️

Validate path parameters:

function isValidUUID(id: string): boolean {
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
  return uuidRegex.test(id);
}

@Get("/:id")
getUser(@Param("id") id: string, @Res() res: ResponseServer) {
  if (!isValidUUID(id)) {
    return res.status(400).json({ error: "Invalid user ID format" });
  }
  
  // Proceed with valid ID
  res.json({ id });
}

Validation Middleware 🔌

Create validation middleware:

import { z } from "zod";
import type { RequestHandler } from "azurajs/types";

export function validateBody(schema: z.ZodSchema): RequestHandler {
  return async (req, res, next) => {
    try {
      req.body = schema.parse(req.body);
      await next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        res.status(400).json({
          error: "Validation failed",
          details: error.errors,
        });
      } else {
        await next();
      }
    }
  };
}

// Usage
const createUserSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

app.post("/api/users", validateBody(createUserSchema), (req, res) => {
  // req.body is validated
  res.json({ user: req.body });
});

Error Messages 📝

Provide clear validation error messages:

const userSchema = z.object({
  name: z.string({
    required_error: "Name is required",
    invalid_type_error: "Name must be a string",
  }).min(2, "Name must be at least 2 characters"),
  
  email: z.string({
    required_error: "Email is required",
  }).email("Invalid email format"),
  
  age: z.number({
    invalid_type_error: "Age must be a number",
  }).positive("Age must be positive").int("Age must be an integer"),
});

Best Practices ✨

Validate early: Validate at the controller level before processing

Consistent responses: Return validation errors in a consistent format

Reuse validators: Create reusable validation functions and schemas

Don't trust client data: Always validate input from clients

Next Steps 📖

On this page