507 lines
8.5 KiB
Markdown
507 lines
8.5 KiB
Markdown
# API Design Patterns Reference
|
|
|
|
## RESTful API Best Practices
|
|
|
|
### Resource Naming
|
|
- Use plural nouns: `/api/users`, `/api/products`
|
|
- Use kebab-case for multi-word resources: `/api/order-items`
|
|
- Avoid verbs in URLs (use HTTP methods instead)
|
|
|
|
### HTTP Methods
|
|
- `GET` - Retrieve resources (safe, idempotent)
|
|
- `POST` - Create new resources
|
|
- `PUT` - Full update (idempotent)
|
|
- `PATCH` - Partial update
|
|
- `DELETE` - Remove resources (idempotent)
|
|
|
|
### Standard Endpoints Pattern
|
|
|
|
```
|
|
GET /api/users - List all (with pagination)
|
|
POST /api/users - Create new
|
|
GET /api/users/:id - Get single
|
|
PUT /api/users/:id - Full update
|
|
PATCH /api/users/:id - Partial update
|
|
DELETE /api/users/:id - Delete
|
|
GET /api/users/:id/posts - Nested resource
|
|
```
|
|
|
|
### Pagination
|
|
|
|
**Query Parameters:**
|
|
```
|
|
GET /api/users?page=1&limit=20
|
|
GET /api/users?offset=0&limit=20
|
|
```
|
|
|
|
**Response Format:**
|
|
```json
|
|
{
|
|
"data": [...],
|
|
"pagination": {
|
|
"page": 1,
|
|
"limit": 20,
|
|
"total": 150,
|
|
"totalPages": 8
|
|
}
|
|
}
|
|
```
|
|
|
|
**Cursor-Based (for large datasets):**
|
|
```
|
|
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
|
|
|
|
Response:
|
|
{
|
|
"data": [...],
|
|
"nextCursor": "eyJpZCI6MTQzfQ",
|
|
"hasMore": true
|
|
}
|
|
```
|
|
|
|
### Filtering
|
|
|
|
```
|
|
GET /api/products?category=electronics&price[gte]=100&price[lte]=500
|
|
GET /api/users?status=active&role=admin
|
|
GET /api/posts?author=123&published=true
|
|
```
|
|
|
|
### Sorting
|
|
|
|
```
|
|
GET /api/products?sort=price # Ascending
|
|
GET /api/products?sort=-price # Descending (minus prefix)
|
|
GET /api/products?sort=category,price # Multiple fields
|
|
```
|
|
|
|
### Searching
|
|
|
|
```
|
|
GET /api/products?q=laptop
|
|
GET /api/users?search=john&fields=name,email
|
|
GET /api/posts?fulltext=artificial+intelligence
|
|
```
|
|
|
|
### Response Formats
|
|
|
|
**Success Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { ... },
|
|
"message": "User created successfully"
|
|
}
|
|
```
|
|
|
|
**Error Response:**
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Invalid input data",
|
|
"details": [
|
|
{
|
|
"field": "email",
|
|
"message": "Email is required"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
**List Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [...],
|
|
"pagination": { ... },
|
|
"meta": {
|
|
"count": 20,
|
|
"total": 150
|
|
}
|
|
}
|
|
```
|
|
|
|
### Status Codes
|
|
|
|
- `200 OK` - Successful GET, PUT, PATCH, DELETE
|
|
- `201 Created` - Successful POST
|
|
- `204 No Content` - Successful DELETE (no response body)
|
|
- `400 Bad Request` - Invalid input
|
|
- `401 Unauthorized` - Missing/invalid authentication
|
|
- `403 Forbidden` - Authenticated but not authorized
|
|
- `404 Not Found` - Resource doesn't exist
|
|
- `409 Conflict` - Resource already exists
|
|
- `422 Unprocessable Entity` - Validation errors
|
|
- `429 Too Many Requests` - Rate limit exceeded
|
|
- `500 Internal Server Error` - Server error
|
|
|
|
## Authentication Patterns
|
|
|
|
### JWT Authentication
|
|
|
|
**Login Endpoint:**
|
|
```javascript
|
|
POST /api/auth/login
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "password123"
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"user": { ... },
|
|
"accessToken": "eyJhbGc...",
|
|
"refreshToken": "eyJhbGc...",
|
|
"expiresIn": 3600
|
|
}
|
|
}
|
|
```
|
|
|
|
**Protected Route Header:**
|
|
```
|
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
|
```
|
|
|
|
**Refresh Token:**
|
|
```javascript
|
|
POST /api/auth/refresh
|
|
{
|
|
"refreshToken": "eyJhbGc..."
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"accessToken": "eyJhbGc...",
|
|
"expiresIn": 3600
|
|
}
|
|
```
|
|
|
|
### API Key Authentication
|
|
|
|
```
|
|
X-API-Key: your-api-key-here
|
|
```
|
|
|
|
### Session-Based Authentication
|
|
|
|
```javascript
|
|
POST /api/auth/login
|
|
Response sets cookie:
|
|
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
|
|
```
|
|
|
|
## Advanced Patterns
|
|
|
|
### Nested Resources
|
|
|
|
```
|
|
GET /api/users/123/posts - User's posts
|
|
POST /api/users/123/posts - Create post for user
|
|
GET /api/posts/456/comments - Post's comments
|
|
```
|
|
|
|
### Bulk Operations
|
|
|
|
```javascript
|
|
POST /api/users/bulk
|
|
{
|
|
"operation": "create",
|
|
"data": [
|
|
{ "name": "User 1", "email": "user1@example.com" },
|
|
{ "name": "User 2", "email": "user2@example.com" }
|
|
]
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"results": [
|
|
{ "id": 1, "status": "created" },
|
|
{ "id": 2, "status": "created" }
|
|
],
|
|
"summary": {
|
|
"total": 2,
|
|
"succeeded": 2,
|
|
"failed": 0
|
|
}
|
|
}
|
|
```
|
|
|
|
### Batch Requests
|
|
|
|
```javascript
|
|
POST /api/batch
|
|
{
|
|
"requests": [
|
|
{ "method": "GET", "url": "/api/users/123" },
|
|
{ "method": "GET", "url": "/api/posts/456" }
|
|
]
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"responses": [
|
|
{ "status": 200, "body": { ... } },
|
|
{ "status": 200, "body": { ... } }
|
|
]
|
|
}
|
|
```
|
|
|
|
### Webhooks
|
|
|
|
```javascript
|
|
POST /api/webhooks
|
|
{
|
|
"url": "https://example.com/webhook",
|
|
"events": ["user.created", "order.completed"],
|
|
"secret": "webhook-secret"
|
|
}
|
|
```
|
|
|
|
### File Upload
|
|
|
|
**Single File:**
|
|
```javascript
|
|
POST /api/files
|
|
Content-Type: multipart/form-data
|
|
|
|
Response:
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "file-123",
|
|
"url": "https://cdn.example.com/files/file-123.jpg",
|
|
"size": 1024000,
|
|
"type": "image/jpeg"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Presigned URL (for direct upload):**
|
|
```javascript
|
|
POST /api/files/presign
|
|
{
|
|
"filename": "photo.jpg",
|
|
"contentType": "image/jpeg"
|
|
}
|
|
|
|
Response:
|
|
{
|
|
"uploadUrl": "https://s3.amazonaws.com/...",
|
|
"fileId": "file-123",
|
|
"expiresIn": 3600
|
|
}
|
|
```
|
|
|
|
### Rate Limiting Headers
|
|
|
|
```
|
|
X-RateLimit-Limit: 1000
|
|
X-RateLimit-Remaining: 999
|
|
X-RateLimit-Reset: 1640995200
|
|
```
|
|
|
|
### Versioning
|
|
|
|
**URL Versioning (recommended):**
|
|
```
|
|
/api/v1/users
|
|
/api/v2/users
|
|
```
|
|
|
|
**Header Versioning:**
|
|
```
|
|
Accept: application/vnd.api.v2+json
|
|
```
|
|
|
|
**Query Parameter:**
|
|
```
|
|
/api/users?version=2
|
|
```
|
|
|
|
## GraphQL Patterns
|
|
|
|
### Query Structure
|
|
|
|
```graphql
|
|
query GetUser($id: ID!) {
|
|
user(id: $id) {
|
|
id
|
|
name
|
|
email
|
|
posts {
|
|
id
|
|
title
|
|
createdAt
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Mutation Structure
|
|
|
|
```graphql
|
|
mutation CreateUser($input: CreateUserInput!) {
|
|
createUser(input: $input) {
|
|
user {
|
|
id
|
|
name
|
|
email
|
|
}
|
|
errors {
|
|
field
|
|
message
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pagination (Relay-style)
|
|
|
|
```graphql
|
|
query GetUsers($first: Int, $after: String) {
|
|
users(first: $first, after: $after) {
|
|
edges {
|
|
node {
|
|
id
|
|
name
|
|
}
|
|
cursor
|
|
}
|
|
pageInfo {
|
|
hasNextPage
|
|
endCursor
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Structured Error Codes
|
|
|
|
```javascript
|
|
const ErrorCodes = {
|
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
AUTHENTICATION_ERROR: 'AUTHENTICATION_ERROR',
|
|
AUTHORIZATION_ERROR: 'AUTHORIZATION_ERROR',
|
|
NOT_FOUND: 'NOT_FOUND',
|
|
DUPLICATE_RESOURCE: 'DUPLICATE_RESOURCE',
|
|
RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',
|
|
INTERNAL_ERROR: 'INTERNAL_ERROR'
|
|
};
|
|
```
|
|
|
|
### Error Response Structure
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Validation failed",
|
|
"details": [
|
|
{
|
|
"field": "email",
|
|
"code": "INVALID_FORMAT",
|
|
"message": "Email format is invalid"
|
|
}
|
|
],
|
|
"requestId": "req-123abc"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Request/Response Examples
|
|
|
|
### Create User
|
|
|
|
```javascript
|
|
POST /api/users
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"password": "SecurePass123!",
|
|
"role": "user"
|
|
}
|
|
|
|
Response (201 Created):
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "user-123",
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"role": "user",
|
|
"createdAt": "2024-12-09T10:30:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Update User
|
|
|
|
```javascript
|
|
PATCH /api/users/user-123
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"name": "John Smith"
|
|
}
|
|
|
|
Response (200 OK):
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "user-123",
|
|
"name": "John Smith",
|
|
"email": "john@example.com",
|
|
"updatedAt": "2024-12-09T11:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Search with Filters
|
|
|
|
```javascript
|
|
GET /api/products?q=laptop&category=electronics&price[gte]=500&sort=-price&page=1&limit=20
|
|
|
|
Response (200 OK):
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"id": "prod-1",
|
|
"name": "Gaming Laptop Pro",
|
|
"category": "electronics",
|
|
"price": 1299.99
|
|
}
|
|
],
|
|
"pagination": {
|
|
"page": 1,
|
|
"limit": 20,
|
|
"total": 45,
|
|
"totalPages": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Always validate input** - Use validation libraries (Joi, Zod, Yup)
|
|
2. **Sanitize data** - Prevent SQL injection, XSS
|
|
3. **Use HTTPS** - Always encrypt in production
|
|
4. **Implement rate limiting** - Prevent abuse
|
|
5. **Set security headers** - Use helmet.js or equivalent
|
|
6. **Validate authentication** - On every protected route
|
|
7. **Use prepared statements** - For database queries
|
|
8. **Log securely** - Don't log sensitive data
|
|
9. **Handle errors safely** - Don't expose internal details
|
|
10. **Keep dependencies updated** - Regular security patches
|