# 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/)