# Joi Validation Guide

## Overview

This guide explains how to use Joi validation in the backend API for consistent request validation across all endpoints.

## Structure

Validators are organized by feature in the `src/middlewares/validators/` directory:

- `joi.ts` - Core validation function and common schemas
- `auth.ts` - Authentication related validations
- `user.ts` - User management validations
- `{model}.ts` - Model-specific validations (generated automatically)

## Core Validation Function

The `validateJoi` function is the main validation middleware that:

- Validates request body, params, and query based on provided schema
- Returns appropriate error responses for validation failures
- Supports custom error messages in Vietnamese

## Creating Validators

For each model/feature, create a validator file with the following pattern:

```typescript
import Joi from "joi";
import { validateJoi } from "../joi";

// Model validation schemas
export const modelSchemas = {
	create: Joi.object({
		body: Joi.object({
			// TODO: Add validation rules for model creation
			// Example:
			// name: Joi.string().min(1).max(100).required(),
			// description: Joi.string().optional(),
		}),
		params: Joi.object(),
		query: Joi.object(),
	}),

	update: Joi.object({
		params: Joi.object({
			id: Joi.string().required(),
		}),
		body: Joi.object({
			// TODO: Add validation rules for model update
			// All fields should be optional for partial updates
		}),
		query: Joi.object(),
	}),
};

// Pre-built validators
export const validateModelCreate = validateJoi(modelSchemas.create);
export const validateModelUpdate = validateJoi(modelSchemas.update);
```

**Note**: The API generator (`pnpm gen-api <model>`) will automatically create this template structure with TODO comments for you to customize.

## Using Validators in Controllers

Apply validators to your controller endpoints:

```typescript
import { validateModelCreate, validateModelUpdate } from "#middlewares/validators/model";

export default (_express: Application) => ({
	post: {
		middleware: [verify, queryModifier, validateModelCreate],
		handler: async (req: Req, res: Res) => {
			// Handler logic
		},
	},
	put: {
		middleware: [verify, queryModifier, validateId, validateModelUpdate],
		handler: async (req: Req, res: Res) => {
			// Handler logic
		},
	},
});
```

## Common Validation Patterns

### Required Fields

```typescript
fieldName: Joi.string().required().messages({
	"any.required": "Field name là bắt buộc",
});
```

### Optional Fields

```typescript
fieldName: Joi.string().optional();
```

### String Validation

```typescript
fieldName: Joi.string().min(1).max(100).trim().required();
```

### Email Validation

```typescript
email: Joi.string().email().required().messages({
	"string.email": "Email không hợp lệ",
});
```

### Number Validation

```typescript
age: Joi.number().integer().min(0).max(150).optional();
```

### Date Validation

```typescript
birthDate: Joi.date().iso().optional();
```

### Array Validation

```typescript
tags: Joi.array().items(Joi.string()).optional();
```

### Enum Validation

```typescript
status: Joi.string().valid("active", "inactive", "pending").required();
```

## Error Messages

- Use Vietnamese for user-facing error messages
- Keep technical error details in English for logging
- Use descriptive field names in error messages

## Auto-generated Validators

When using the API generator (`pnpm gen-api <model>`), validators are automatically created and applied to:

- **POST endpoints**: `validateModelCreate`
- **PUT endpoints**: `validateId` + `validateModelUpdate`
- **GET /{id} endpoints**: `validateId`
- **GET / endpoints**: No Joi validation (handled by `queryModifier` for standard parameters)

The generator will:

1. Create validator file in `src/middlewares/validators/{model}.ts` (if not exists)
2. Generate controllers with proper validator imports and middleware
3. Provide TODO comments in validator schemas for you to customize validation rules

**Note**: For custom query parameter validation on GET endpoints, manually add validation schemas and apply the validator to the controller middleware.

## Testing Validation

Test your validators with various inputs:

- Valid data
- Missing required fields
- Invalid data types
- Boundary values
- Malformed requests

## Best Practices

1. Always validate input data
2. Use appropriate validation rules for each field type
3. Provide clear, localized error messages
4. Keep validation schemas organized by feature
5. Test validation thoroughly
6. Update validators when API contracts change

### Query Parameters

- **Standard parameters** (handled by `queryModifier` middleware):
  - `page`: Integer, minimum 1
  - `pageSize`: Integer, minimum 1, maximum 100
  - `filters`: String (Sequelize filter conditions)
  - `sortField`: String (field to sort by)
  - `sortOrder`: String, values: 'asc', 'desc'
- **Custom parameters** (validate in Joi):
  - `search`: String, maximum 255 characters, trimmed
  - `status`: Valid enum values matching your model

### Integration with Query Modifier

When using both Joi validation and `queryModifier` middleware:

1. Joi validates parameter types and custom business rules (when applied)
2. `queryModifier` processes standard pagination/filtering parameters
3. Controllers receive processed data in `req.payload`

**Note**: Auto-generated GET endpoints don't include Joi validation by default. Add custom validators manually if needed for specific query parameters.

### Custom Query Parameters

When adding custom query parameters beyond the standard ones, create a custom validator:

**Recommended to validate:**

- Enum values (status, type, category, etc.)
- Numeric ranges with constraints
- Date formats
- Boolean flags

**Optional to validate:**

- Basic strings (search, name, etc.) - if only length/format validation is needed
- Simple IDs - if already validated elsewhere

**Example custom validator:**

```typescript
// In your validator file
export const validateModelGetAll = validateJoi({
    params: Joi.object(),
    body: Joi.object(),
    query: Joi.object({
        // Recommended: Validate enum values
        status: Joi.string().valid('active', 'inactive', 'suspended').optional(),
        category: Joi.string().valid('electronics', 'books', 'clothing').optional(),

        // Optional: Basic string validation
        search: Joi.string().max(255).optional().trim(),

        // Recommended: Numeric constraints
        minPrice: Joi.number().min(0).optional(),
        maxPrice: Joi.number().min(0).optional(),
    }),
});

// In your controller
import { validateModelGetAll } from "#middlewares/validators/model";

get: {
    middleware: [verify, queryModifier, validateModelGetAll], // Add this if custom validation needed
    handler: async (req: Req, res: Res) => {
        // Handler logic
    },
},
```
