Files
CleanArchitecture-template/.brain/.agent/skills/engineering-advanced-skills/api-design-reviewer/references/rest_design_rules.md
2026-03-12 15:17:52 +07:00

10 KiB

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

{
  "data": {
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com"
  },
  "meta": {
    "timestamp": "2024-02-16T13:00:00Z",
    "version": "1.0"
  }
}

Collection Responses

{
  "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

{
  "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

 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:

 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:
{
  "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

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)

{
  "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"
    }
  }
}
  • 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