# API Response Guide

## Overview

API Response is a unified response handling system for backend-template, inspired by fosco-erp-backend. This system provides:

- Consistent response formatting for both success and error cases
- Multi-language support (Vietnamese and English)
- Automatic response formatting
- Easy extensibility
- Support for Joi validation, database errors, and custom errors

## GenericError Structure

```typescript
export class GenericError {
	message: Record<Language, string>; // Multi-language error messages
	type: string; // Error type (e.g., "VALIDATION_ERROR", "BAD_REQUEST")
	code: number; // HTTP status code
	additionalData?: unknown; // Additional data (optional)
}
```

## Usage

### 1. Import GenericError

```typescript
import { GenericError } from "#interfaces/error/generic";
```

### 2. Throw GenericError in Controller

```typescript
// Example validation error
if (!isValid) {
	throw new GenericError({ vi: "Giá trị không hợp lệ", en: "Invalid value" }, "BAD_REQUEST", 400);
}

// Example with additional data
if (errors.length > 0) {
	throw new GenericError({ vi: "Dữ liệu đầu vào không hợp lệ", en: "Invalid input data" }, "VALIDATION_ERROR", 400, {
		errors: errors.array(),
	});
}
```

### 3. Use res.error() in catch block

```typescript
try {
	// Business logic
	if (someCondition) {
		throw new GenericError(
			{ vi: "Điều kiện không thỏa mãn", en: "Condition not satisfied" },
			"BUSINESS_LOGIC_ERROR",
			422,
		);
	}
} catch (error) {
	// Automatically handle GenericError and format response
	res.error(error);
}
```

## Response Format

### Success Response

When using `res.sendOk()`, the response will have a standard format:

```json
{
	"message": "User created successfully",
	"message_en": "User created successfully",
	"responseData": {
		"id": "123",
		"email": "user@example.com"
	},
	"status": "success",
	"timeStamp": "2025-12-07 10:00:00",
	"violations": null
}
```

### List Response (with pagination)

For list endpoints with pagination:

```json
{
	"message": null,
	"message_en": null,
	"responseData": {
		"rows": [
			{ "id": "123", "email": "user1@example.com" },
			{ "id": "124", "email": "user2@example.com" }
		],
		"count": 50,
		"page": 1,
		"pageSize": 10
	},
	"status": "success",
	"timeStamp": "2025-12-07 10:00:00",
	"violations": null
}
```

### Single Item Response

For single item endpoints:

```json
{
	"message": null,
	"message_en": null,
	"responseData": {
		"id": "123",
		"email": "user@example.com",
		"first_name": "John"
	},
	"status": "success",
	"timeStamp": "2025-12-07 10:00:00",
	"violations": null
}
```

## Error Processors

The system includes multiple error handlers for different error types:

- **JoiValidateErrorHandler**: Handles Joi validation errors
- **DatabaseErrorHandler**: Handles Sequelize database errors (ValidationError, UniqueConstraintError, ForeignKeyConstraintError)
- **GenericErrorHandler**: Fallback handler for GenericError and unknown errors

## Common Error Types

| Type                    | Code | Description           |
| ----------------------- | ---- | --------------------- |
| `BAD_REQUEST`           | 400  | Invalid input data    |
| `VALIDATION_ERROR`      | 400  | Validation error      |
| `UNAUTHORIZED`          | 401  | Not authenticated     |
| `FORBIDDEN`             | 403  | Access denied         |
| `NOT_FOUND`             | 404  | Resource not found    |
| `CONFLICT`              | 409  | Data conflict         |
| `BUSINESS_LOGIC_ERROR`  | 422  | Business logic error  |
| `INTERNAL_SERVER_ERROR` | 500  | Internal server error |

## Real Examples

### Get Users (List with pagination)

```typescript
export const getUsers = [
	...getUsersValidation,
	async (req: Request, res: Response) => {
		try {
			const errors = validationResult(req);
			if (!errors.isEmpty()) {
				throw new GenericError(
					{ vi: "Dữ liệu đầu vào không hợp lệ", en: "Invalid input data" },
					"VALIDATION_ERROR",
					400,
					{ errors: errors.array() },
				);
			}

			const result = await UserProvider.findAll({
				page: parseInt(req.query.page as string) || 1,
				limit: parseInt(req.query.limit as string) || 10,
				// ... other filters
			});

			return res.sendOk({
				data: {
					rows: result.users,
					count: result.total,
					page: result.page,
					pageSize: result.limit,
				},
			});
		} catch (error) {
			res.error(error);
		}
	},
];
```

### Get Single User

```typescript
export const getUser = [
	param("id").isUUID(),
	async (req: Request, res: Response) => {
		try {
			const user = await UserProvider.findById(req.params.id);

			if (!user) {
				throw new GenericError({ vi: "Người dùng không tồn tại", en: "User not found" }, "NOT_FOUND", 404);
			}

			return res.sendOk({ data: user });
		} catch (error) {
			res.error(error);
		}
	},
];
```

### Create User

```typescript
export const createUser = [
	...createUserValidation,
	async (req: Request, res: Response) => {
		try {
			const errors = validationResult(req);
			if (!errors.isEmpty()) {
				throw new GenericError(
					{ vi: "Dữ liệu đầu vào không hợp lệ", en: "Invalid input data" },
					"VALIDATION_ERROR",
					400,
					{ errors: errors.array() },
				);
			}

			const user = await UserProvider.create(userData);

			return res.sendOk({
				data: user,
				message: "User created successfully",
				statusCode: 201,
			});
		} catch (error) {
			res.error(error);
		}
	},
];
```

### Validation with GenericError

```typescript
// Instead of res.status(400).json()
if (value < 0 || value > 100) {
	throw new GenericError(
		{ vi: "Giá trị phải từ 0 đến 100", en: "Value must be between 0 and 100" },
		"VALIDATION_ERROR",
		400,
	);
}
```

## Benefits

1. **Consistency**: All errors have the same format
2. **Multi-language**: Automatic vi/en support based on `accept-language` header
3. **Easy debugging**: `additionalData` can contain detailed debug information
4. **Automatic handling**: `res.error()` automatically formats response
5. **Extensible**: Easy to add new error handler types
6. **Database error handling**: Automatic handling of common database errors

## Best Practices

1. **Use clear messages**: Describe exactly what error occurred
2. **Choose appropriate codes**: Use standard HTTP status codes
3. **Add additionalData when needed**: Helps with debugging and client-side error handling
4. **Throw immediately when error detected**: Don't continue processing when an error occurs
5. **Use res.error() in catch**: To ensure unified handling

## Migration from Old Approach

### Before (old way):

```typescript
if (!errors.isEmpty()) {
    return res.sendErrorStatus({
        status: 400,
        message: "Error message",
        err: new MeUError(...)
    });
}
```

### After (GenericError):

```typescript
if (!errors.isEmpty()) {
	throw new GenericError({ vi: "Dữ liệu không hợp lệ", en: "Invalid data" }, "VALIDATION_ERROR", 400, {
		errors: errors.array(),
	});
}
```

And in catch block:

```typescript
} catch (error) {
    res.error(error);  // Automatic handling
}
```

## Related Files

- `src/interfaces/error/generic.ts` - GenericError class definition
- `src/interfaces/error/violations/IViolations.ts` - Error processor
- `src/interfaces/error/violations/ViolationImpl.ts` - GenericError handler
- `src/interfaces/error/handler/joi-error-handler.ts` - Joi validation handler
- `src/interfaces/error/handler/database-error-handler.ts` - Database error handler
- `src/middlewares/response.ts` - res.error() implementation
- `src/middlewares/validator.ts` - Updated validator middleware
- `src/interfaces/IApi.ts` - Res interface with error method
