# API Design - Detailed Reference

This reference contains detailed code examples for RESTful API design patterns.

## Standard Response Format

### Success Response

```typescript
interface SuccessResponse<T> {
  success: true;
  data: T;
  metadata?: {
    timestamp: string;
    version: string;
    requestId: string;
  };
  pagination?: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

// Example
{
  "success": true,
  "data": {
    "id": "123",
    "email": "user@example.com",
    "name": "John Doe"
  },
  "metadata": {
    "timestamp": "2024-01-01T00:00:00Z",
    "version": "1.0.0",
    "requestId": "req_abc123"
  }
}
```

### Error Response

```typescript
interface ErrorResponse {
  success: false;
  error: {
    code: string;
    message: string;
    details?: any;
    field?: string;
    stack?: string; // Only in development
  };
  metadata?: {
    timestamp: string;
    requestId: string;
  };
}

// Example
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "field": "email",
    "details": {
      "provided": "invalid-email",
      "expected": "valid email address"
    }
  }
}
```

## DTOs (Data Transfer Objects)

### Request DTOs

```typescript
// create.dto.ts
import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  @IsNotEmpty()
  email: string;

  @MinLength(6)
  @IsNotEmpty()
  password: string;

  @IsOptional()
  name?: string;
}

// update.dto.ts
export class UpdateUserDto {
  @IsEmail()
  @IsOptional()
  email?: string;

  @IsOptional()
  name?: string;

  @IsOptional()
  avatar?: string;
}

// query.dto.ts
export class QueryUsersDto {
  @IsOptional()
  @Type(() => Number)
  @Min(1)
  page?: number = 1;

  @IsOptional()
  @Type(() => Number)
  @Min(1)
  @Max(100)
  limit?: number = 10;

  @IsOptional()
  search?: string;

  @IsOptional()
  @IsIn(['createdAt', 'name', 'email'])
  sortBy?: string = 'createdAt';

  @IsOptional()
  @IsIn(['asc', 'desc'])
  order?: 'asc' | 'desc' = 'desc';
}
```

### Response DTOs

```typescript
// user.response.dto.ts
export class UserResponseDto {
  id: string;
  email: string;
  name: string;
  avatar?: string;
  role: string;
  createdAt: Date;
  updatedAt: Date;

  // Hide sensitive data
  static fromEntity(user: User): UserResponseDto {
    const { password, ...data } = user;
    return data;
  }
}

// paginated.response.dto.ts
export class PaginatedResponseDto<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}
```

## Controller Implementation

```typescript
// user.controller.ts
@Controller('users')
@ApiTags('Users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  @ApiOperation({ summary: 'List users' })
  @ApiQuery({ type: QueryUsersDto })
  @ApiResponse({ status: 200, type: PaginatedResponseDto })
  async list(@Query() query: QueryUsersDto): Promise<ResponseDto> {
    const { data, total } = await this.userService.findAll(query);

    return {
      success: true,
      data: data.map(UserResponseDto.fromEntity),
      pagination: {
        page: query.page,
        limit: query.limit,
        total,
        totalPages: Math.ceil(total / query.limit)
      }
    };
  }

  @Get(':id')
  @ApiOperation({ summary: 'Get user by ID' })
  @ApiParam({ name: 'id', type: 'string' })
  @ApiResponse({ status: 200, type: UserResponseDto })
  @ApiResponse({ status: 404, description: 'User not found' })
  async getById(@Param('id') id: string): Promise<ResponseDto> {
    const user = await this.userService.findById(id);

    if (!user) {
      throw new HttpException(
        {
          success: false,
          error: {
            code: 'USER_NOT_FOUND',
            message: `User with ID ${id} not found`
          }
        },
        HttpStatus.NOT_FOUND
      );
    }

    return {
      success: true,
      data: UserResponseDto.fromEntity(user)
    };
  }

  @Post()
  @ApiOperation({ summary: 'Create user' })
  @ApiBody({ type: CreateUserDto })
  @ApiResponse({ status: 201, type: UserResponseDto })
  async create(@Body() dto: CreateUserDto): Promise<ResponseDto> {
    const user = await this.userService.create(dto);

    return {
      success: true,
      data: UserResponseDto.fromEntity(user)
    };
  }

  @Put(':id')
  @ApiOperation({ summary: 'Update user' })
  @UseGuards(AuthGuard)
  async update(
    @Param('id') id: string,
    @Body() dto: UpdateUserDto
  ): Promise<ResponseDto> {
    const user = await this.userService.update(id, dto);

    return {
      success: true,
      data: UserResponseDto.fromEntity(user)
    };
  }

  @Delete(':id')
  @ApiOperation({ summary: 'Delete user' })
  @UseGuards(AuthGuard, RolesGuard)
  @Roles('admin')
  async delete(@Param('id') id: string): Promise<ResponseDto> {
    await this.userService.delete(id);

    return {
      success: true,
      data: { deleted: true }
    };
  }
}
```

## OpenAPI/Swagger Documentation

```yaml
# openapi/user-service.yaml
openapi: 3.0.0
info:
  title: User Service API
  version: 1.0.0
  description: User management endpoints
servers:
  - url: https://api.goodgo.com/v1
paths:
  /users:
    get:
      summary: List users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 10
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
    post:
      summary: Create user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: User created
        '400':
          description: Validation error
```

## Pagination Pattern

```typescript
// pagination.service.ts
export class PaginationService {
  paginate<T>(
    query: any,
    options: {
      page: number;
      limit: number;
      sortBy?: string;
      order?: 'asc' | 'desc';
    }
  ) {
    const skip = (options.page - 1) * options.limit;

    return {
      skip,
      take: options.limit,
      orderBy: options.sortBy ? {
        [options.sortBy]: options.order || 'desc'
      } : undefined
    };
  }
}
```

## Error Handling

```typescript
// error.middleware.ts
export function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) {
  const isDev = process.env.NODE_ENV === 'development';

  // Known errors
  if (err instanceof ValidationError) {
    return res.status(400).json({
      success: false,
      error: {
        code: 'VALIDATION_ERROR',
        message: err.message,
        details: err.errors
      }
    });
  }

  if (err instanceof UnauthorizedError) {
    return res.status(401).json({
      success: false,
      error: {
        code: 'UNAUTHORIZED',
        message: 'Authentication required'
      }
    });
  }

  // Unknown errors
  logger.error('Unhandled error:', err);

  res.status(500).json({
    success: false,
    error: {
      code: 'INTERNAL_ERROR',
      message: isDev ? err.message : 'Internal server error',
      stack: isDev ? err.stack : undefined
    }
  });
}
```

## Zod Validation Alternative

```typescript
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6),
  name: z.string().optional()
});

const QueryUsersSchema = z.object({
  page: z.coerce.number().min(1).default(1),
  limit: z.coerce.number().min(1).max(100).default(10),
  search: z.string().optional(),
  sortBy: z.enum(['createdAt', 'name', 'email']).default('createdAt'),
  order: z.enum(['asc', 'desc']).default('desc')
});

// Usage in controller
async create(req: Request, res: Response) {
  const result = CreateUserSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({
      success: false,
      error: { code: 'VALIDATION_ERROR', details: result.error.issues }
    });
  }
  // ... create user with result.data
}
```
