add brain
This commit is contained in:
@@ -0,0 +1,680 @@
|
||||
# Common API Anti-Patterns and How to Avoid Them
|
||||
|
||||
## Introduction
|
||||
|
||||
This document outlines common anti-patterns in REST API design that can lead to poor developer experience, maintenance nightmares, and scalability issues. Each anti-pattern is accompanied by examples and recommended solutions.
|
||||
|
||||
## 1. Verb-Based URLs (The RPC Trap)
|
||||
|
||||
### Anti-Pattern
|
||||
Using verbs in URLs instead of treating endpoints as resources.
|
||||
|
||||
```
|
||||
❌ Bad Examples:
|
||||
POST /api/getUsers
|
||||
POST /api/createUser
|
||||
GET /api/deleteUser/123
|
||||
POST /api/updateUserPassword
|
||||
GET /api/calculateOrderTotal/456
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Violates REST principles
|
||||
- Makes the API feel like RPC instead of REST
|
||||
- HTTP methods lose their semantic meaning
|
||||
- Reduces cacheability
|
||||
- Harder to understand resource relationships
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
GET /api/users # Get users
|
||||
POST /api/users # Create user
|
||||
DELETE /api/users/123 # Delete user
|
||||
PATCH /api/users/123/password # Update password
|
||||
GET /api/orders/456/total # Get order total
|
||||
```
|
||||
|
||||
## 2. Inconsistent Naming Conventions
|
||||
|
||||
### Anti-Pattern
|
||||
Mixed naming conventions across the API.
|
||||
|
||||
```json
|
||||
❌ Bad Examples:
|
||||
{
|
||||
"user_id": 123, // snake_case
|
||||
"firstName": "John", // camelCase
|
||||
"last-name": "Doe", // kebab-case
|
||||
"EMAIL": "john@example.com", // UPPER_CASE
|
||||
"IsActive": true // PascalCase
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Confuses developers
|
||||
- Increases cognitive load
|
||||
- Makes code generation difficult
|
||||
- Reduces API adoption
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Choose one convention and stick to it (camelCase recommended):
|
||||
{
|
||||
"userId": 123,
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"email": "john@example.com",
|
||||
"isActive": true
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Ignoring HTTP Status Codes
|
||||
|
||||
### Anti-Pattern
|
||||
Always returning HTTP 200 regardless of the actual result.
|
||||
|
||||
```json
|
||||
❌ Bad Example:
|
||||
HTTP/1.1 200 OK
|
||||
{
|
||||
"status": "error",
|
||||
"code": 404,
|
||||
"message": "User not found"
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Breaks HTTP semantics
|
||||
- Prevents proper error handling by clients
|
||||
- Breaks caching and proxies
|
||||
- Makes monitoring and debugging harder
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Good Example:
|
||||
HTTP/1.1 404 Not Found
|
||||
{
|
||||
"error": {
|
||||
"code": "USER_NOT_FOUND",
|
||||
"message": "User with ID 123 not found",
|
||||
"requestId": "req-abc123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Overly Complex Nested Resources
|
||||
|
||||
### Anti-Pattern
|
||||
Creating deeply nested URL structures that are hard to navigate.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
/companies/123/departments/456/teams/789/members/012/projects/345/tasks/678/comments/901
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- URLs become unwieldy
|
||||
- Creates tight coupling between resources
|
||||
- Makes independent resource access difficult
|
||||
- Complicates authorization logic
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
/tasks/678 # Direct access to task
|
||||
/tasks/678/comments # Task comments
|
||||
/users/012/tasks # User's tasks
|
||||
/projects/345?team=789 # Project filtering
|
||||
```
|
||||
|
||||
## 5. Inconsistent Error Response Formats
|
||||
|
||||
### Anti-Pattern
|
||||
Different error response structures across endpoints.
|
||||
|
||||
```json
|
||||
❌ Bad Examples:
|
||||
# Endpoint 1
|
||||
{"error": "Invalid email"}
|
||||
|
||||
# Endpoint 2
|
||||
{"success": false, "msg": "User not found", "code": 404}
|
||||
|
||||
# Endpoint 3
|
||||
{"errors": [{"field": "name", "message": "Required"}]}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Makes error handling complex for clients
|
||||
- Reduces code reusability
|
||||
- Poor developer experience
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Standardized Error Format:
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "The request contains invalid data",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"code": "INVALID_FORMAT",
|
||||
"message": "Email address is not valid"
|
||||
}
|
||||
],
|
||||
"requestId": "req-123456",
|
||||
"timestamp": "2024-02-16T13:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Missing or Poor Pagination
|
||||
|
||||
### Anti-Pattern
|
||||
Returning all results in a single response or inconsistent pagination.
|
||||
|
||||
```json
|
||||
❌ Bad Examples:
|
||||
# No pagination (returns 10,000 records)
|
||||
GET /api/users
|
||||
|
||||
# Inconsistent pagination parameters
|
||||
GET /api/users?page=1&size=10
|
||||
GET /api/orders?offset=0&limit=20
|
||||
GET /api/products?start=0&count=50
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Can cause performance issues
|
||||
- May overwhelm clients
|
||||
- Inconsistent pagination parameters confuse developers
|
||||
- No way to estimate total results
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Good Example:
|
||||
GET /api/users?page=1&pageSize=10
|
||||
|
||||
{
|
||||
"data": [...],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"pageSize": 10,
|
||||
"total": 150,
|
||||
"totalPages": 15,
|
||||
"hasNext": true,
|
||||
"hasPrev": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Exposing Internal Implementation Details
|
||||
|
||||
### Anti-Pattern
|
||||
URLs and field names that reflect database structure or internal architecture.
|
||||
|
||||
```
|
||||
❌ Bad Examples:
|
||||
/api/user_table/123
|
||||
/api/db_orders
|
||||
/api/legacy_customer_data
|
||||
/api/temp_migration_users
|
||||
|
||||
Response fields:
|
||||
{
|
||||
"user_id_pk": 123,
|
||||
"internal_ref_code": "usr_abc",
|
||||
"db_created_timestamp": 1645123456
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Couples API to internal implementation
|
||||
- Makes refactoring difficult
|
||||
- Exposes unnecessary technical details
|
||||
- Reduces API longevity
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
/api/users/123
|
||||
/api/orders
|
||||
/api/customers
|
||||
|
||||
Response fields:
|
||||
{
|
||||
"id": 123,
|
||||
"referenceCode": "usr_abc",
|
||||
"createdAt": "2024-02-16T13:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Overloading Single Endpoint
|
||||
|
||||
### Anti-Pattern
|
||||
Using one endpoint for multiple unrelated operations based on request parameters.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
POST /api/user-actions
|
||||
{
|
||||
"action": "create_user",
|
||||
"userData": {...}
|
||||
}
|
||||
|
||||
POST /api/user-actions
|
||||
{
|
||||
"action": "delete_user",
|
||||
"userId": 123
|
||||
}
|
||||
|
||||
POST /api/user-actions
|
||||
{
|
||||
"action": "send_email",
|
||||
"userId": 123,
|
||||
"emailType": "welcome"
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Breaks REST principles
|
||||
- Makes documentation complex
|
||||
- Complicates client implementation
|
||||
- Reduces discoverability
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
POST /api/users # Create user
|
||||
DELETE /api/users/123 # Delete user
|
||||
POST /api/users/123/emails # Send email to user
|
||||
```
|
||||
|
||||
## 9. Lack of Versioning Strategy
|
||||
|
||||
### Anti-Pattern
|
||||
Making breaking changes without version management.
|
||||
|
||||
```
|
||||
❌ Bad Examples:
|
||||
# Original API
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 30
|
||||
}
|
||||
|
||||
# Later (breaking change with no versioning)
|
||||
{
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"birthDate": "1994-02-16"
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Breaks existing clients
|
||||
- Forces all clients to update simultaneously
|
||||
- No graceful migration path
|
||||
- Reduces API stability
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
# Version 1
|
||||
GET /api/v1/users/123
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 30
|
||||
}
|
||||
|
||||
# Version 2 (with both versions supported)
|
||||
GET /api/v2/users/123
|
||||
{
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"birthDate": "1994-02-16",
|
||||
"age": 30 // Backwards compatibility
|
||||
}
|
||||
```
|
||||
|
||||
## 10. Poor Error Messages
|
||||
|
||||
### Anti-Pattern
|
||||
Vague, unhelpful, or technical error messages.
|
||||
|
||||
```json
|
||||
❌ Bad Examples:
|
||||
{"error": "Something went wrong"}
|
||||
{"error": "Invalid input"}
|
||||
{"error": "SQL constraint violation: FK_user_profile_id"}
|
||||
{"error": "NullPointerException at line 247"}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Doesn't help developers fix issues
|
||||
- Increases support burden
|
||||
- Poor developer experience
|
||||
- May expose sensitive information
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Good Examples:
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "The email address is required and must be in a valid format",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"code": "REQUIRED",
|
||||
"message": "Email address is required"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 11. Ignoring Content Negotiation
|
||||
|
||||
### Anti-Pattern
|
||||
Hard-coding response format without considering client preferences.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
# Always returns JSON regardless of Accept header
|
||||
GET /api/users/123
|
||||
Accept: application/xml
|
||||
# Returns JSON anyway
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Reduces API flexibility
|
||||
- Ignores HTTP standards
|
||||
- Makes integration harder for diverse clients
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Example:
|
||||
GET /api/users/123
|
||||
Accept: application/xml
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<user>
|
||||
<id>123</id>
|
||||
<name>John Doe</name>
|
||||
</user>
|
||||
```
|
||||
|
||||
## 12. Stateful API Design
|
||||
|
||||
### Anti-Pattern
|
||||
Maintaining session state on the server between requests.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
# Step 1: Initialize session
|
||||
POST /api/session/init
|
||||
|
||||
# Step 2: Set context (requires step 1)
|
||||
POST /api/session/set-user/123
|
||||
|
||||
# Step 3: Get data (requires steps 1 & 2)
|
||||
GET /api/session/user-data
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Breaks REST statelessness principle
|
||||
- Reduces scalability
|
||||
- Makes caching difficult
|
||||
- Complicates error recovery
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Example:
|
||||
# Self-contained requests
|
||||
GET /api/users/123/data
|
||||
Authorization: Bearer jwt-token-with-context
|
||||
```
|
||||
|
||||
## 13. Inconsistent HTTP Method Usage
|
||||
|
||||
### Anti-Pattern
|
||||
Using HTTP methods inappropriately or inconsistently.
|
||||
|
||||
```
|
||||
❌ Bad Examples:
|
||||
GET /api/users/123/delete # DELETE operation with GET
|
||||
POST /api/users/123/get # GET operation with POST
|
||||
PUT /api/users # Creating with PUT on collection
|
||||
GET /api/users/search # Search with side effects
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Violates HTTP semantics
|
||||
- Breaks caching and idempotency expectations
|
||||
- Confuses developers and tools
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
DELETE /api/users/123 # Delete with DELETE
|
||||
GET /api/users/123 # Get with GET
|
||||
POST /api/users # Create on collection
|
||||
GET /api/users?q=search # Safe search with GET
|
||||
```
|
||||
|
||||
## 14. Missing Rate Limiting Information
|
||||
|
||||
### Anti-Pattern
|
||||
Not providing rate limiting information to clients.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
{
|
||||
"error": "Rate limit exceeded"
|
||||
}
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Clients don't know when to retry
|
||||
- No information about current limits
|
||||
- Difficult to implement proper backoff strategies
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Example:
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 0
|
||||
X-RateLimit-Reset: 1640995200
|
||||
Retry-After: 3600
|
||||
|
||||
{
|
||||
"error": {
|
||||
"code": "RATE_LIMIT_EXCEEDED",
|
||||
"message": "API rate limit exceeded",
|
||||
"retryAfter": 3600
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 15. Chatty API Design
|
||||
|
||||
### Anti-Pattern
|
||||
Requiring multiple API calls to accomplish common tasks.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
# Get user profile requires 4 API calls
|
||||
GET /api/users/123 # Basic info
|
||||
GET /api/users/123/profile # Profile details
|
||||
GET /api/users/123/settings # User settings
|
||||
GET /api/users/123/stats # User statistics
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Increases latency
|
||||
- Creates network overhead
|
||||
- Makes mobile apps inefficient
|
||||
- Complicates client implementation
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Examples:
|
||||
# Single call with expansion
|
||||
GET /api/users/123?include=profile,settings,stats
|
||||
|
||||
# Or provide composite endpoints
|
||||
GET /api/users/123/dashboard
|
||||
|
||||
# Or batch operations
|
||||
POST /api/batch
|
||||
{
|
||||
"requests": [
|
||||
{"method": "GET", "url": "/users/123"},
|
||||
{"method": "GET", "url": "/users/123/profile"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 16. No Input Validation
|
||||
|
||||
### Anti-Pattern
|
||||
Accepting and processing invalid input without proper validation.
|
||||
|
||||
```json
|
||||
❌ Bad Example:
|
||||
POST /api/users
|
||||
{
|
||||
"email": "not-an-email",
|
||||
"age": -5,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
# API processes this and fails later or stores invalid data
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Leads to data corruption
|
||||
- Security vulnerabilities
|
||||
- Difficult to debug issues
|
||||
- Poor user experience
|
||||
|
||||
### Solution
|
||||
```json
|
||||
✅ Good Example:
|
||||
POST /api/users
|
||||
{
|
||||
"email": "not-an-email",
|
||||
"age": -5,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
HTTP/1.1 400 Bad Request
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "The request contains invalid data",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"code": "INVALID_FORMAT",
|
||||
"message": "Email must be a valid email address"
|
||||
},
|
||||
{
|
||||
"field": "age",
|
||||
"code": "INVALID_RANGE",
|
||||
"message": "Age must be between 0 and 150"
|
||||
},
|
||||
{
|
||||
"field": "name",
|
||||
"code": "REQUIRED",
|
||||
"message": "Name is required and cannot be empty"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 17. Synchronous Long-Running Operations
|
||||
|
||||
### Anti-Pattern
|
||||
Blocking the client with long-running operations in synchronous endpoints.
|
||||
|
||||
```
|
||||
❌ Bad Example:
|
||||
POST /api/reports/generate
|
||||
# Client waits 30 seconds for response
|
||||
```
|
||||
|
||||
### Why It's Bad
|
||||
- Poor user experience
|
||||
- Timeouts and connection issues
|
||||
- Resource waste on client and server
|
||||
- Doesn't scale well
|
||||
|
||||
### Solution
|
||||
```
|
||||
✅ Good Example:
|
||||
# Async pattern
|
||||
POST /api/reports
|
||||
HTTP/1.1 202 Accepted
|
||||
Location: /api/reports/job-123
|
||||
{
|
||||
"jobId": "job-123",
|
||||
"status": "processing",
|
||||
"estimatedCompletion": "2024-02-16T13:05:00Z"
|
||||
}
|
||||
|
||||
# Check status
|
||||
GET /api/reports/job-123
|
||||
{
|
||||
"jobId": "job-123",
|
||||
"status": "completed",
|
||||
"result": "/api/reports/download/report-456"
|
||||
}
|
||||
```
|
||||
|
||||
## Prevention Strategies
|
||||
|
||||
### 1. API Design Reviews
|
||||
- Implement mandatory design reviews
|
||||
- Use checklists based on these anti-patterns
|
||||
- Include multiple stakeholders
|
||||
|
||||
### 2. API Style Guides
|
||||
- Create and enforce API style guides
|
||||
- Use linting tools for consistency
|
||||
- Regular training for development teams
|
||||
|
||||
### 3. Automated Testing
|
||||
- Test for common anti-patterns
|
||||
- Include contract testing
|
||||
- Monitor API usage patterns
|
||||
|
||||
### 4. Documentation Standards
|
||||
- Require comprehensive API documentation
|
||||
- Include examples and error scenarios
|
||||
- Keep documentation up-to-date
|
||||
|
||||
### 5. Client Feedback
|
||||
- Regularly collect feedback from API consumers
|
||||
- Monitor API usage analytics
|
||||
- Conduct developer experience surveys
|
||||
|
||||
## Conclusion
|
||||
|
||||
Avoiding these anti-patterns requires:
|
||||
- Understanding REST principles
|
||||
- Consistent design standards
|
||||
- Regular review and refactoring
|
||||
- Focus on developer experience
|
||||
- Proper tooling and automation
|
||||
|
||||
Remember: A well-designed API is an asset that grows in value over time, while a poorly designed API becomes a liability that hampers development and adoption.
|
||||
@@ -0,0 +1,487 @@
|
||||
# REST API Design Rules Reference
|
||||
|
||||
## Core Principles
|
||||
|
||||
### 1. Resources, Not Actions
|
||||
REST APIs should focus on **resources** (nouns) rather than **actions** (verbs). The HTTP methods provide the actions.
|
||||
|
||||
```
|
||||
✅ Good:
|
||||
GET /users # Get all users
|
||||
GET /users/123 # Get user 123
|
||||
POST /users # Create new user
|
||||
PUT /users/123 # Update user 123
|
||||
DELETE /users/123 # Delete user 123
|
||||
|
||||
❌ Bad:
|
||||
POST /getUsers
|
||||
POST /createUser
|
||||
POST /updateUser/123
|
||||
POST /deleteUser/123
|
||||
```
|
||||
|
||||
### 2. Hierarchical Resource Structure
|
||||
Use hierarchical URLs to represent resource relationships:
|
||||
|
||||
```
|
||||
/users/123/orders/456/items/789
|
||||
```
|
||||
|
||||
But avoid excessive nesting (max 3-4 levels):
|
||||
|
||||
```
|
||||
❌ Too deep: /companies/123/departments/456/teams/789/members/012/tasks/345
|
||||
✅ Better: /tasks/345?member=012&team=789
|
||||
```
|
||||
|
||||
## Resource Naming Conventions
|
||||
|
||||
### URLs Should Use Kebab-Case
|
||||
```
|
||||
✅ Good:
|
||||
/user-profiles
|
||||
/order-items
|
||||
/shipping-addresses
|
||||
|
||||
❌ Bad:
|
||||
/userProfiles
|
||||
/user_profiles
|
||||
/orderItems
|
||||
```
|
||||
|
||||
### Collections vs Individual Resources
|
||||
```
|
||||
Collection: /users
|
||||
Individual: /users/123
|
||||
Sub-resource: /users/123/orders
|
||||
```
|
||||
|
||||
### Pluralization Rules
|
||||
- Use **plural nouns** for collections: `/users`, `/orders`
|
||||
- Use **singular nouns** for single resources: `/user-profile`, `/current-session`
|
||||
- Be consistent throughout your API
|
||||
|
||||
## HTTP Methods Usage
|
||||
|
||||
### GET - Safe and Idempotent
|
||||
- **Purpose**: Retrieve data
|
||||
- **Safe**: No side effects
|
||||
- **Idempotent**: Multiple calls return same result
|
||||
- **Request Body**: Should not have one
|
||||
- **Cacheable**: Yes
|
||||
|
||||
```
|
||||
GET /users/123
|
||||
GET /users?status=active&limit=10
|
||||
```
|
||||
|
||||
### POST - Not Idempotent
|
||||
- **Purpose**: Create resources, non-idempotent operations
|
||||
- **Safe**: No
|
||||
- **Idempotent**: No
|
||||
- **Request Body**: Usually required
|
||||
- **Cacheable**: Generally no
|
||||
|
||||
```
|
||||
POST /users # Create new user
|
||||
POST /users/123/activate # Activate user (action)
|
||||
```
|
||||
|
||||
### PUT - Idempotent
|
||||
- **Purpose**: Create or completely replace a resource
|
||||
- **Safe**: No
|
||||
- **Idempotent**: Yes
|
||||
- **Request Body**: Required (complete resource)
|
||||
- **Cacheable**: No
|
||||
|
||||
```
|
||||
PUT /users/123 # Replace entire user resource
|
||||
```
|
||||
|
||||
### PATCH - Partial Update
|
||||
- **Purpose**: Partially update a resource
|
||||
- **Safe**: No
|
||||
- **Idempotent**: Not necessarily
|
||||
- **Request Body**: Required (partial resource)
|
||||
- **Cacheable**: No
|
||||
|
||||
```
|
||||
PATCH /users/123 # Update only specified fields
|
||||
```
|
||||
|
||||
### DELETE - Idempotent
|
||||
- **Purpose**: Remove a resource
|
||||
- **Safe**: No
|
||||
- **Idempotent**: Yes (same result if called multiple times)
|
||||
- **Request Body**: Usually not needed
|
||||
- **Cacheable**: No
|
||||
|
||||
```
|
||||
DELETE /users/123
|
||||
```
|
||||
|
||||
## Status Codes
|
||||
|
||||
### Success Codes (2xx)
|
||||
- **200 OK**: Standard success response
|
||||
- **201 Created**: Resource created successfully (POST)
|
||||
- **202 Accepted**: Request accepted for processing (async)
|
||||
- **204 No Content**: Success with no response body (DELETE, PUT)
|
||||
|
||||
### Redirection Codes (3xx)
|
||||
- **301 Moved Permanently**: Resource permanently moved
|
||||
- **302 Found**: Temporary redirect
|
||||
- **304 Not Modified**: Use cached version
|
||||
|
||||
### Client Error Codes (4xx)
|
||||
- **400 Bad Request**: Invalid request syntax or data
|
||||
- **401 Unauthorized**: Authentication required
|
||||
- **403 Forbidden**: Access denied (user authenticated but not authorized)
|
||||
- **404 Not Found**: Resource not found
|
||||
- **405 Method Not Allowed**: HTTP method not supported
|
||||
- **409 Conflict**: Resource conflict (duplicates, version mismatch)
|
||||
- **422 Unprocessable Entity**: Valid syntax but semantic errors
|
||||
- **429 Too Many Requests**: Rate limit exceeded
|
||||
|
||||
### Server Error Codes (5xx)
|
||||
- **500 Internal Server Error**: Unexpected server error
|
||||
- **502 Bad Gateway**: Invalid response from upstream server
|
||||
- **503 Service Unavailable**: Server temporarily unavailable
|
||||
- **504 Gateway Timeout**: Upstream server timeout
|
||||
|
||||
## URL Design Patterns
|
||||
|
||||
### Query Parameters for Filtering
|
||||
```
|
||||
GET /users?status=active
|
||||
GET /users?role=admin&department=engineering
|
||||
GET /orders?created_after=2024-01-01&status=pending
|
||||
```
|
||||
|
||||
### Pagination Parameters
|
||||
```
|
||||
# Offset-based
|
||||
GET /users?offset=20&limit=10
|
||||
|
||||
# Cursor-based
|
||||
GET /users?cursor=eyJpZCI6MTIzfQ&limit=10
|
||||
|
||||
# Page-based
|
||||
GET /users?page=3&page_size=10
|
||||
```
|
||||
|
||||
### Sorting Parameters
|
||||
```
|
||||
GET /users?sort=created_at # Ascending
|
||||
GET /users?sort=-created_at # Descending (prefix with -)
|
||||
GET /users?sort=last_name,first_name # Multiple fields
|
||||
```
|
||||
|
||||
### Field Selection
|
||||
```
|
||||
GET /users?fields=id,name,email
|
||||
GET /users/123?include=orders,profile
|
||||
GET /users/123?exclude=internal_notes
|
||||
```
|
||||
|
||||
### Search Parameters
|
||||
```
|
||||
GET /users?q=john
|
||||
GET /products?search=laptop&category=electronics
|
||||
```
|
||||
|
||||
## Response Format Standards
|
||||
|
||||
### Consistent Response Structure
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 123,
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com"
|
||||
},
|
||||
"meta": {
|
||||
"timestamp": "2024-02-16T13:00:00Z",
|
||||
"version": "1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Collection Responses
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{"id": 1, "name": "Item 1"},
|
||||
{"id": 2, "name": "Item 2"}
|
||||
],
|
||||
"pagination": {
|
||||
"total": 150,
|
||||
"page": 1,
|
||||
"pageSize": 10,
|
||||
"totalPages": 15,
|
||||
"hasNext": true,
|
||||
"hasPrev": false
|
||||
},
|
||||
"meta": {
|
||||
"timestamp": "2024-02-16T13:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response Format
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "The request contains invalid parameters",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"code": "INVALID_FORMAT",
|
||||
"message": "Email address is not valid"
|
||||
}
|
||||
],
|
||||
"requestId": "req-123456",
|
||||
"timestamp": "2024-02-16T13:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Field Naming Conventions
|
||||
|
||||
### Use camelCase for JSON Fields
|
||||
```json
|
||||
✅ Good:
|
||||
{
|
||||
"firstName": "John",
|
||||
"lastName": "Doe",
|
||||
"createdAt": "2024-02-16T13:00:00Z",
|
||||
"isActive": true
|
||||
}
|
||||
|
||||
❌ Bad:
|
||||
{
|
||||
"first_name": "John",
|
||||
"LastName": "Doe",
|
||||
"created-at": "2024-02-16T13:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Boolean Fields
|
||||
Use positive, clear names with "is", "has", "can", or "should" prefixes:
|
||||
|
||||
```json
|
||||
✅ Good:
|
||||
{
|
||||
"isActive": true,
|
||||
"hasPermission": false,
|
||||
"canEdit": true,
|
||||
"shouldNotify": false
|
||||
}
|
||||
|
||||
❌ Bad:
|
||||
{
|
||||
"active": true,
|
||||
"disabled": false, // Double negative
|
||||
"permission": false // Unclear meaning
|
||||
}
|
||||
```
|
||||
|
||||
### Date/Time Fields
|
||||
- Use ISO 8601 format: `2024-02-16T13:00:00Z`
|
||||
- Include timezone information
|
||||
- Use consistent field naming:
|
||||
|
||||
```json
|
||||
{
|
||||
"createdAt": "2024-02-16T13:00:00Z",
|
||||
"updatedAt": "2024-02-16T13:30:00Z",
|
||||
"deletedAt": null,
|
||||
"publishedAt": "2024-02-16T14:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Content Negotiation
|
||||
|
||||
### Accept Headers
|
||||
```
|
||||
Accept: application/json
|
||||
Accept: application/xml
|
||||
Accept: application/json; version=1
|
||||
```
|
||||
|
||||
### Content-Type Headers
|
||||
```
|
||||
Content-Type: application/json
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
### Versioning via Headers
|
||||
```
|
||||
Accept: application/vnd.myapi.v1+json
|
||||
API-Version: 1.0
|
||||
```
|
||||
|
||||
## Caching Guidelines
|
||||
|
||||
### Cache-Control Headers
|
||||
```
|
||||
Cache-Control: public, max-age=3600 # Cache for 1 hour
|
||||
Cache-Control: private, max-age=0 # Don't cache
|
||||
Cache-Control: no-cache, must-revalidate # Always validate
|
||||
```
|
||||
|
||||
### ETags for Conditional Requests
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
ETag: "123456789"
|
||||
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
|
||||
|
||||
# Client subsequent request:
|
||||
If-None-Match: "123456789"
|
||||
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
|
||||
```
|
||||
|
||||
## Security Headers
|
||||
|
||||
### Authentication
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
Authorization: Basic dXNlcjpwYXNzd29yZA==
|
||||
Authorization: Api-Key abc123def456
|
||||
```
|
||||
|
||||
### CORS Headers
|
||||
```
|
||||
Access-Control-Allow-Origin: https://example.com
|
||||
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### Rate Limit Headers
|
||||
```
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 999
|
||||
X-RateLimit-Reset: 1640995200
|
||||
X-RateLimit-Window: 3600
|
||||
```
|
||||
|
||||
### Rate Limit Exceeded Response
|
||||
```json
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
Retry-After: 3600
|
||||
|
||||
{
|
||||
"error": {
|
||||
"code": "RATE_LIMIT_EXCEEDED",
|
||||
"message": "API rate limit exceeded",
|
||||
"details": {
|
||||
"limit": 1000,
|
||||
"window": "1 hour",
|
||||
"retryAfter": 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hypermedia (HATEOAS)
|
||||
|
||||
### Links in Responses
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/users/123"
|
||||
},
|
||||
"orders": {
|
||||
"href": "/users/123/orders"
|
||||
},
|
||||
"edit": {
|
||||
"href": "/users/123",
|
||||
"method": "PUT"
|
||||
},
|
||||
"delete": {
|
||||
"href": "/users/123",
|
||||
"method": "DELETE"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Link Relations
|
||||
- **self**: Link to the resource itself
|
||||
- **edit**: Link to edit the resource
|
||||
- **delete**: Link to delete the resource
|
||||
- **related**: Link to related resources
|
||||
- **next/prev**: Pagination links
|
||||
|
||||
## Common Anti-Patterns to Avoid
|
||||
|
||||
### 1. Verbs in URLs
|
||||
```
|
||||
❌ Bad: /api/getUser/123
|
||||
✅ Good: GET /api/users/123
|
||||
```
|
||||
|
||||
### 2. Inconsistent Naming
|
||||
```
|
||||
❌ Bad: /user-profiles and /userAddresses
|
||||
✅ Good: /user-profiles and /user-addresses
|
||||
```
|
||||
|
||||
### 3. Deep Nesting
|
||||
```
|
||||
❌ Bad: /companies/123/departments/456/teams/789/members/012
|
||||
✅ Good: /team-members/012?team=789
|
||||
```
|
||||
|
||||
### 4. Ignoring HTTP Status Codes
|
||||
```
|
||||
❌ Bad: Always return 200 with error info in body
|
||||
✅ Good: Use appropriate status codes (404, 400, 500, etc.)
|
||||
```
|
||||
|
||||
### 5. Exposing Internal Structure
|
||||
```
|
||||
❌ Bad: /api/database_table_users
|
||||
✅ Good: /api/users
|
||||
```
|
||||
|
||||
### 6. No Versioning Strategy
|
||||
```
|
||||
❌ Bad: Breaking changes without version management
|
||||
✅ Good: /api/v1/users or Accept: application/vnd.api+json;version=1
|
||||
```
|
||||
|
||||
### 7. Inconsistent Error Responses
|
||||
```
|
||||
❌ Bad: Different error formats for different endpoints
|
||||
✅ Good: Standardized error response structure
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Use nouns for resources, not verbs**
|
||||
2. **Leverage HTTP methods correctly**
|
||||
3. **Maintain consistent naming conventions**
|
||||
4. **Implement proper error handling**
|
||||
5. **Use appropriate HTTP status codes**
|
||||
6. **Design for cacheability**
|
||||
7. **Implement security from the start**
|
||||
8. **Plan for versioning**
|
||||
9. **Provide comprehensive documentation**
|
||||
10. **Follow HATEOAS principles when applicable**
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [RFC 7231 - HTTP/1.1 Semantics and Content](https://tools.ietf.org/html/rfc7231)
|
||||
- [RFC 6570 - URI Template](https://tools.ietf.org/html/rfc6570)
|
||||
- [OpenAPI Specification](https://swagger.io/specification/)
|
||||
- [REST API Design Best Practices](https://www.restapitutorial.com/)
|
||||
- [HTTP Status Code Definitions](https://httpstatuses.com/)
|
||||
Reference in New Issue
Block a user