Files
pos-system/.agent/rules/api-design.md

5.0 KiB

trigger
trigger
always_on

RESTful API Design Standards

When to Use This Skill

Use this skill when:

  • Creating new API endpoints
  • Designing request/response DTOs
  • Implementing controllers and routes
  • Writing OpenAPI/Swagger documentation
  • Standardizing error responses
  • Implementing pagination, filtering, and sorting
  • Setting up API versioning

Core Principles

  1. Consistency: All APIs follow the same patterns
  2. Predictability: Developers can guess endpoint behavior
  3. Simplicity: Easy to understand and use
  4. Documentation: Self-documenting through OpenAPI
  5. Error Handling: Clear, actionable error messages

URL Structure

https://api.goodgo.com/v1/{resource}/{id}/{sub-resource}

GET    /v1/users                 # List users
POST   /v1/users                 # Create user
GET    /v1/users/123             # Get user by ID
PUT    /v1/users/123             # Update user
DELETE /v1/users/123             # Delete user
GET    /v1/users/123/orders      # Get user's orders

HTTP Methods

  • GET: Retrieve resource(s) - Safe, Idempotent
  • POST: Create new resource - Not idempotent
  • PUT: Full update - Idempotent
  • PATCH: Partial update - Idempotent
  • DELETE: Remove resource - Idempotent

Standard Response Format

// Success
interface SuccessResponse<T> {
  success: true;
  data: T;
  pagination?: { page: number; limit: number; total: number; totalPages: number };
}

// Error
interface ErrorResponse {
  success: false;
  error: { code: string; message: string; details?: any; field?: string };
}

Key Patterns

Request DTO

export class CreateUserDto {
  @IsEmail() @IsNotEmpty() email: string;
  @MinLength(6) @IsNotEmpty() password: string;
  @IsOptional() name?: string;
}

export class QueryUsersDto {
  @IsOptional() @Min(1) page?: number = 1;
  @IsOptional() @Min(1) @Max(100) limit?: number = 10;
  @IsOptional() search?: string;
  @IsOptional() @IsIn(['createdAt', 'name']) sortBy?: string = 'createdAt';
  @IsOptional() @IsIn(['asc', 'desc']) order?: 'asc' | 'desc' = 'desc';
}

Controller

@Get()
async list(@Query() query: QueryUsersDto) {
  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')
async getById(@Param('id') id: string) {
  const user = await this.userService.findById(id);
  if (!user) throw new NotFoundException({ success: false, error: { code: 'NOT_FOUND', message: 'User not found' } });
  return { success: true, data: UserResponseDto.fromEntity(user) };
}

Best Practices

  • Resource Naming: Use plural nouns (/users), kebab-case for multi-word
  • Versioning: Include version in URL (/v1/users), maintain backward compatibility
  • Security: Use HTTPS, implement rate limiting, validate all inputs
  • Performance: Implement pagination, use field filtering, cache responses
  • Documentation: Keep OpenAPI spec up to date, include examples

Common Mistakes

  1. Using Verbs in URLs: Non-RESTful endpoints

    # BAD: POST /api/v1/createUser
    # GOOD: POST /api/v1/users
    
  2. Inconsistent Response Format: Different structures for different endpoints

    // BAD: res.json({ user: data }) vs res.json({ result: data })
    // GOOD: res.json({ success: true, data })
    
  3. Wrong HTTP Status Codes: Using 200 for errors

    // BAD: res.status(200).json({ error: 'Not found' });
    // GOOD: res.status(404).json({ success: false, error: { code: 'NOT_FOUND' } });
    
  4. Missing Pagination: Returning all records

    // BAD: prisma.user.findMany()
    // GOOD: prisma.user.findMany({ skip: (page - 1) * limit, take: limit })
    

Quick Reference

HTTP Method Action Idempotent Status Codes
GET Retrieve Yes 200, 404
POST Create No 201, 400, 409
PUT Full update Yes 200, 400, 404
PATCH Partial update Yes 200, 400, 404
DELETE Remove Yes 204, 404

Response Format:

// Success: { success: true, data: T, pagination?: {...} }
// Error: { success: false, error: { code: string, message: string } }

Common Error Codes:

  • 400 - Bad Request (validation)
  • 401 - Unauthorized (no token)
  • 403 - Forbidden (no permission)
  • 404 - Not Found
  • 409 - Conflict (duplicate)
  • 422 - Unprocessable (business rule)
  • 429 - Rate limited

Resources