Claude
Skills
Sign in
Back

api-best-practices

Included with Lifetime
$97 forever

REST API design patterns, OpenAPI specifications, versioning strategies, authentication, error handling, and security best practices. Use when designing APIs, creating endpoints, documenting APIs, or implementing backend services that expose HTTP APIs.

Design

What this skill does


# API Best Practices

This skill provides comprehensive guidance for designing, implementing, and documenting RESTful APIs following industry best practices.

## RESTful Design Principles

### Resource-Oriented Design

APIs should be designed around resources (nouns), not actions (verbs):

**Good**:
```http
GET    /api/v1/users
POST   /api/v1/users
GET    /api/v1/users/{id}
PUT    /api/v1/users/{id}
DELETE /api/v1/users/{id}
```

**Bad**:
```http
GET    /api/v1/getUsers
POST   /api/v1/createUser
POST   /api/v1/updateUser
POST   /api/v1/deleteUser
```

### HTTP Methods and Their Meanings

- **GET**: Retrieve a resource (safe, idempotent, cacheable)
- **POST**: Create a new resource (not idempotent)
- **PUT**: Replace entire resource (idempotent)
- **PATCH**: Partial update (not necessarily idempotent)
- **DELETE**: Remove a resource (idempotent)

### HTTP Status Codes

**Success (2xx)**:
- `200 OK`: Successful GET, PUT, PATCH, DELETE
- `201 Created`: Successful POST with resource creation
- `202 Accepted`: Request accepted for async processing
- `204 No Content`: Successful DELETE or update with no response body

**Client Errors (4xx)**:
- `400 Bad Request`: Malformed request, validation error
- `401 Unauthorized`: Authentication required
- `403 Forbidden`: Authenticated but not authorized
- `404 Not Found`: Resource doesn't exist
- `409 Conflict`: Resource conflict (duplicate, version mismatch)
- `422 Unprocessable Entity`: Valid syntax but semantic errors
- `429 Too Many Requests`: Rate limit exceeded

**Server Errors (5xx)**:
- `500 Internal Server Error`: Unexpected server error
- `502 Bad Gateway`: Upstream service failure
- `503 Service Unavailable`: Temporary overload or maintenance
- `504 Gateway Timeout`: Upstream timeout

## API Versioning

### URL Versioning (Recommended)
```http
GET /api/v1/users
GET /api/v2/users
```

**Pros**: Clear, easy to route, visible in logs
**Cons**: Duplicate code across versions

### Header Versioning
```http
GET /api/users
Accept: application/vnd.myapi.v1+json
```

**Pros**: Clean URLs
**Cons**: Harder to test, less visible

### Version Management Rules
1. Never break backwards compatibility in same version
2. Deprecate old versions with advance notice (6-12 months)
3. Document migration guides between versions
4. Support at least 2 major versions simultaneously

## Request/Response Patterns

### Standard Request Format

**JSON Request Body**:
```json
{
  "email": "[email protected]",
  "name": "John Doe",
  "preferences": {
    "newsletter": true,
    "notifications": false
  }
}
```

**Query Parameters** (for filtering, pagination, sorting):
```http
GET /api/v1/users?role=admin&status=active&page=2&limit=20&sort=-created_at
```

### Standard Response Format

**Success Response**:
```json
{
  "data": {
    "id": "user_123",
    "email": "[email protected]",
    "name": "John Doe",
    "createdAt": "2025-10-16T10:30:00Z"
  }
}
```

**Error Response**:
```json
{
  "error": {
    "code": "INVALID_EMAIL",
    "message": "Email address is invalid",
    "field": "email",
    "details": "Email must contain @ symbol"
  }
}
```

**Collection Response with Pagination**:
```json
{
  "data": [
    { "id": 1, "name": "User 1" },
    { "id": 2, "name": "User 2" }
  ],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 156,
    "totalPages": 8,
    "hasNext": true,
    "hasPrev": true
  },
  "links": {
    "self": "/api/v1/users?page=2",
    "next": "/api/v1/users?page=3",
    "prev": "/api/v1/users?page=1",
    "first": "/api/v1/users?page=1",
    "last": "/api/v1/users?page=8"
  }
}
```

## Authentication Patterns

### JWT (JSON Web Tokens)

**Login Flow**:
```http
POST /api/v1/auth/login
{
  "email": "[email protected]",
  "password": "SecurePassword123"
}

Response (200):
{
  "accessToken": "eyJhbGc...",
  "refreshToken": "eyJhbGc...",
  "expiresIn": 900
}
```

**Using Access Token**:
```http
GET /api/v1/users/me
Authorization: Bearer eyJhbGc...
```

**Token Refresh**:
```http
POST /api/v1/auth/refresh
{
  "refreshToken": "eyJhbGc..."
}

Response (200):
{
  "accessToken": "eyJhbGc...",
  "expiresIn": 900
}
```

### API Keys

**Header-based** (recommended):
```http
GET /api/v1/data
X-API-Key: sk_live_abc123xyz
```

**Query parameter** (less secure, use only for public data):
```http
GET /api/v1/public-data?api_key=sk_live_abc123xyz
```

### OAuth 2.0 Flows

**Authorization Code Flow** (for web apps):
1. Redirect to `/oauth/authorize`
2. User grants permission
3. Receive authorization code
4. Exchange code for access token at `/oauth/token`
5. Use access token for API requests

**Client Credentials Flow** (for server-to-server):
```http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=abc&client_secret=xyz
```

## Error Handling

### Validation Errors

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "errors": [
      {
        "field": "email",
        "message": "Email is required"
      },
      {
        "field": "age",
        "message": "Age must be at least 18"
      }
    ]
  }
}
```

### Business Logic Errors

```json
{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Account balance too low for this transaction",
    "details": {
      "balance": 50.00,
      "required": 100.00,
      "currency": "USD"
    }
  }
}
```

### Rate Limiting Errors

```http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1634400000
Retry-After: 3600

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded",
    "retryAfter": 3600
  }
}
```

## Pagination Strategies

### Offset Pagination (Simple)
```http
GET /api/v1/users?offset=40&limit=20
```

**Pros**: Simple, allows jumping to any page
**Cons**: Performance degrades with large offsets, inconsistent if data changes

### Cursor Pagination (Recommended for large datasets)
```http
GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20

Response:
{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6MTQzfQ",
    "hasMore": true
  }
}
```

**Pros**: Consistent results, performant at any scale
**Cons**: Can't jump to specific page

### Page-Number Pagination (User-friendly)
```http
GET /api/v1/users?page=3&limit=20
```

**Pros**: User-friendly, easy to understand
**Cons**: Same issues as offset pagination

## Rate Limiting

### Implementation Pattern

**Headers to include**:
```http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1634400000
```

**Tiered Limits**:
- Anonymous: 100 requests/hour
- Basic tier: 1,000 requests/hour
- Pro tier: 10,000 requests/hour
- Enterprise: Custom limits

### Rate Limiting Algorithms

**Token Bucket** (recommended):
- Allows bursts
- Smooth long-term rate
- Most flexible

**Fixed Window**:
- Simple to implement
- Can allow double limit at window boundaries
- Less flexible

**Sliding Window**:
- More accurate than fixed window
- More complex to implement
- Better user experience

## API Security Best Practices

### 1. Always Use HTTPS
Never send sensitive data over HTTP. Enforce HTTPS at the load balancer level.

### 2. Validate All Inputs
```python
from pydantic import BaseModel, EmailStr, constr

class UserCreate(BaseModel):
    email: EmailStr
    password: constr(min_length=8, max_length=100)
    name: constr(min_length=1, max_length=100)
```

### 3. Sanitize Outputs
Prevent injection attacks by escaping output:
```python
import html
safe_output = html.escape(user_input)
```

### 4. Use Parameterized Queries
```python
# ✅ SAFE - Parameterized
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))

# ❌ UNSAFE - String concatenation
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
```

### 5. Implement CORS Properly
```python
# Be specific with origins
CORS(app, origins=["https://myapp.com", "https://app.myapp.com"])

# ❌ NEVER use wildcard in production
# CORS(app, origins=["*"])  # DA

Related in Design