172 lines
5.0 KiB
Markdown
172 lines
5.0 KiB
Markdown
---
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
@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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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:**
|
|
```typescript
|
|
// 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
|
|
|
|
- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) - Official OpenAPI docs
|
|
- [REST API Design](https://restfulapi.net/) - REST best practices
|
|
- [Detailed Code Examples](./references/REFERENCE.md)
|
|
- [API Versioning Strategy](../api-versioning-strategy/SKILL.md) - Versioning patterns
|
|
- [API Gateway Advanced](../api-gateway-advanced/SKILL.md) - Gateway patterns
|
|
- [Middleware Patterns](../middleware-patterns/SKILL.md) - Request handling
|