Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Design RESTful and GraphQL APIs following industry best practices for consistency, versioning, and developer experience.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/rest-best-practices.md
1# REST API Best Practices23## URL Structure45### Resource Naming67```8# Good - Plural nouns9GET /api/users10GET /api/orders11GET /api/products1213# Bad - Verbs or mixed conventions14GET /api/getUser15GET /api/user (inconsistent singular)16POST /api/createOrder17```1819### Nested Resources2021```22# Shallow nesting (preferred)23GET /api/users/{id}/orders24GET /api/orders/{id}2526# Deep nesting (avoid)27GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews28# Better:29GET /api/order-items/{id}/reviews30```3132## HTTP Methods and Status Codes3334### GET - Retrieve Resources3536```37GET /api/users → 200 OK (with list)38GET /api/users/{id} → 200 OK or 404 Not Found39GET /api/users?page=2 → 200 OK (paginated)40```4142### POST - Create Resources4344```45POST /api/users46Body: {"name": "John", "email": "[email protected]"}47→ 201 Created48Location: /api/users/12349Body: {"id": "123", "name": "John", ...}5051POST /api/users (validation error)52→ 422 Unprocessable Entity53Body: {"errors": [...]}54```5556### PUT - Replace Resources5758```59PUT /api/users/{id}60Body: {complete user object}61→ 200 OK (updated)62→ 404 Not Found (doesn't exist)6364# Must include ALL fields65```6667### PATCH - Partial Update6869```70PATCH /api/users/{id}71Body: {"name": "Jane"} (only changed fields)72→ 200 OK73→ 404 Not Found74```7576### DELETE - Remove Resources7778```79DELETE /api/users/{id}80→ 204 No Content (deleted)81→ 404 Not Found82→ 409 Conflict (can't delete due to references)83```8485## Filtering, Sorting, and Searching8687### Query Parameters8889```90# Filtering91GET /api/users?status=active92GET /api/users?role=admin&status=active9394# Sorting95GET /api/users?sort=created_at96GET /api/users?sort=-created_at (descending)97GET /api/users?sort=name,created_at9899# Searching100GET /api/users?search=john101GET /api/users?q=john102103# Field selection (sparse fieldsets)104GET /api/users?fields=id,name,email105```106107## Pagination Patterns108109### Offset-Based Pagination110111```python112GET /api/users?page=2&page_size=20113114Response:115{116"items": [...],117"page": 2,118"page_size": 20,119"total": 150,120"pages": 8121}122```123124### Cursor-Based Pagination (for large datasets)125126```python127GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ128129Response:130{131"items": [...],132"next_cursor": "eyJpZCI6MTQzfQ",133"has_more": true134}135```136137### Link Header Pagination (RESTful)138139```140GET /api/users?page=2141142Response Headers:143Link: <https://api.example.com/users?page=3>; rel="next",144<https://api.example.com/users?page=1>; rel="prev",145<https://api.example.com/users?page=1>; rel="first",146<https://api.example.com/users?page=8>; rel="last"147```148149## Versioning Strategies150151### URL Versioning (Recommended)152153```154/api/v1/users155/api/v2/users156157Pros: Clear, easy to route158Cons: Multiple URLs for same resource159```160161### Header Versioning162163```164GET /api/users165Accept: application/vnd.api+json; version=2166167Pros: Clean URLs168Cons: Less visible, harder to test169```170171### Query Parameter172173```174GET /api/users?version=2175176Pros: Easy to test177Cons: Optional parameter can be forgotten178```179180## Rate Limiting181182### Headers183184```185X-RateLimit-Limit: 1000186X-RateLimit-Remaining: 742187X-RateLimit-Reset: 1640000000188189Response when limited:190429 Too Many Requests191Retry-After: 3600192```193194### Implementation Pattern195196```python197from fastapi import HTTPException, Request198from datetime import datetime, timedelta199200class RateLimiter:201def __init__(self, calls: int, period: int):202self.calls = calls203self.period = period204self.cache = {}205206def check(self, key: str) -> bool:207now = datetime.now()208if key not in self.cache:209self.cache[key] = []210211# Remove old requests212self.cache[key] = [213ts for ts in self.cache[key]214if now - ts < timedelta(seconds=self.period)215]216217if len(self.cache[key]) >= self.calls:218return False219220self.cache[key].append(now)221return True222223limiter = RateLimiter(calls=100, period=60)224225@app.get("/api/users")226async def get_users(request: Request):227if not limiter.check(request.client.host):228raise HTTPException(229status_code=429,230headers={"Retry-After": "60"}231)232return {"users": [...]}233```234235## Authentication and Authorization236237### Bearer Token238239```240Authorization: Bearer eyJhbGciOiJIUzI1NiIs...241242401 Unauthorized - Missing/invalid token243403 Forbidden - Valid token, insufficient permissions244```245246### API Keys247248```249X-API-Key: your-api-key-here250```251252## Error Response Format253254### Consistent Structure255256```json257{258"error": {259"code": "VALIDATION_ERROR",260"message": "Request validation failed",261"details": [262{263"field": "email",264"message": "Invalid email format",265"value": "not-an-email"266}267],268"timestamp": "2025-10-16T12:00:00Z",269"path": "/api/users"270}271}272```273274### Status Code Guidelines275276- `200 OK`: Successful GET, PATCH, PUT277- `201 Created`: Successful POST278- `204 No Content`: Successful DELETE279- `400 Bad Request`: Malformed request280- `401 Unauthorized`: Authentication required281- `403 Forbidden`: Authenticated but not authorized282- `404 Not Found`: Resource doesn't exist283- `409 Conflict`: State conflict (duplicate email, etc.)284- `422 Unprocessable Entity`: Validation errors285- `429 Too Many Requests`: Rate limited286- `500 Internal Server Error`: Server error287- `503 Service Unavailable`: Temporary downtime288289## Caching290291### Cache Headers292293```294# Client caching295Cache-Control: public, max-age=3600296297# No caching298Cache-Control: no-cache, no-store, must-revalidate299300# Conditional requests301ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"302If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"303→ 304 Not Modified304```305306## Bulk Operations307308### Batch Endpoints309310```python311POST /api/users/batch312{313"items": [314{"name": "User1", "email": "[email protected]"},315{"name": "User2", "email": "[email protected]"}316]317}318319Response:320{321"results": [322{"id": "1", "status": "created"},323{"id": null, "status": "failed", "error": "Email already exists"}324]325}326```327328## Idempotency329330### Idempotency Keys331332```333POST /api/orders334Idempotency-Key: unique-key-123335336If duplicate request:337→ 200 OK (return cached response)338```339340## CORS Configuration341342```python343from fastapi.middleware.cors import CORSMiddleware344345app.add_middleware(346CORSMiddleware,347allow_origins=["https://example.com"],348allow_credentials=True,349allow_methods=["*"],350allow_headers=["*"],351)352```353354## Documentation with OpenAPI355356```python357from fastapi import FastAPI358359app = FastAPI(360title="My API",361description="API for managing users",362version="1.0.0",363docs_url="/docs",364redoc_url="/redoc"365)366367@app.get(368"/api/users/{user_id}",369summary="Get user by ID",370response_description="User details",371tags=["Users"]372)373async def get_user(374user_id: str = Path(..., description="The user ID")375):376"""377Retrieve user by ID.378379Returns full user profile including:380- Basic information381- Contact details382- Account status383"""384pass385```386387## Health and Monitoring Endpoints388389```python390@app.get("/health")391async def health_check():392return {393"status": "healthy",394"version": "1.0.0",395"timestamp": datetime.now().isoformat()396}397398@app.get("/health/detailed")399async def detailed_health():400return {401"status": "healthy",402"checks": {403"database": await check_database(),404"redis": await check_redis(),405"external_api": await check_external_api()406}407}408```409