Miles0sage

api-design-patterns

0
0
# Install this skill:
npx skills add Miles0sage/claude-ultimate-stack --skill "api-design-patterns"

Install specific skill from multi-skill repository

# Description

API design patterns including REST conventions, versioning, pagination (cursor and offset), error envelopes, rate limiting, authentication (JWT and OAuth), OpenAPI specification, and SDK generation. Use when designing or reviewing HTTP APIs.

# SKILL.md


name: api-design-patterns
description: API design patterns including REST conventions, versioning, pagination (cursor and offset), error envelopes, rate limiting, authentication (JWT and OAuth), OpenAPI specification, and SDK generation. Use when designing or reviewing HTTP APIs.
origin: ECC


API Design Patterns

Standards and patterns for designing consistent, developer-friendly HTTP APIs.

When to Activate

  • Designing a new REST API
  • Reviewing API endpoint design or response formats
  • Implementing pagination, error handling, or auth
  • Writing OpenAPI specifications
  • User mentions "API design", "REST", "endpoints", "pagination", or "API versioning"

URL Structure and Conventions

# Resources are nouns, plural
GET    /api/v1/users              # List users
POST   /api/v1/users              # Create user
GET    /api/v1/users/{id}         # Get user
PATCH  /api/v1/users/{id}         # Partial update
PUT    /api/v1/users/{id}         # Full replace
DELETE /api/v1/users/{id}         # Delete user

# Sub-resources for relationships
GET    /api/v1/users/{id}/orders  # List user's orders
POST   /api/v1/users/{id}/orders  # Create order for user

# Actions as sub-resources (when CRUD doesn't fit)
POST   /api/v1/users/{id}/verify  # Trigger verification
POST   /api/v1/orders/{id}/cancel # Cancel order

Rules:
- Use kebab-case for multi-word resources: /api/v1/line-items
- Never use verbs in URLs (except action sub-resources)
- Use query params for filtering: GET /users?status=active&role=admin

Versioning

# URL path versioning (recommended for simplicity)
GET /api/v1/users
GET /api/v2/users

# Header versioning (alternative)
GET /api/users
Accept: application/vnd.myapp.v2+json

Rule: Use URL path versioning. Increment major version only for breaking changes. Support N-1 version for at least 6 months.

Response Envelope

{
  "success": true,
  "data": {
    "id": "usr_abc123",
    "name": "Alice",
    "email": "[email protected]",
    "created_at": "2026-03-15T10:30:00Z"
  },
  "meta": null,
  "error": null
}

Error Response

{
  "success": false,
  "data": null,
  "meta": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more fields are invalid.",
    "details": [
      { "field": "email", "message": "Must be a valid email address" },
      { "field": "age", "message": "Must be at least 18" }
    ]
  }
}

HTTP Status Codes

Code When to Use
200 Successful GET, PATCH, PUT
201 Successful POST (resource created)
204 Successful DELETE (no body)
400 Validation error, malformed request
401 Missing or invalid authentication
403 Authenticated but not authorized
404 Resource not found
409 Conflict (duplicate, state mismatch)
422 Semantically invalid (understood but unprocessable)
429 Rate limited
500 Internal server error (never leak stack traces)

Pagination

GET /api/v1/posts?limit=20&cursor=eyJpZCI6MTAwfQ

Response:
{
  "success": true,
  "data": [...],
  "meta": {
    "limit": 20,
    "has_more": true,
    "next_cursor": "eyJpZCI6MTIwfQ",
    "prev_cursor": "eyJpZCI6MTAxfQ"
  }
}

Offset-Based (Simpler, for Small Datasets)

GET /api/v1/users?page=3&limit=20

Response:
{
  "success": true,
  "data": [...],
  "meta": {
    "total": 142,
    "page": 3,
    "limit": 20,
    "total_pages": 8
  }
}

Rule: Default to cursor-based for feeds, timelines, and datasets > 10k rows. Use offset for admin panels and small collections.

Rate Limiting

# Response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1710500000
Retry-After: 30

# 429 response
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 30 seconds.",
    "retry_after": 30
  }
}

Tiers: Unauthenticated 30/min, Free 100/min, Pro 1000/min, Enterprise custom.

Authentication

JWT Bearer Token

# Login
POST /api/v1/auth/login
{ "email": "[email protected]", "password": "..." }

Response:
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
    "expires_in": 900,
    "token_type": "Bearer"
  }
}

# Authenticated request
GET /api/v1/users/me
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Rules:
- Access tokens: short-lived (15 min)
- Refresh tokens: long-lived (7-30 days), stored securely, rotated on use
- Use RS256 (asymmetric) for microservices, HS256 for monoliths
- Never store tokens in localStorage; use httpOnly cookies for web

OAuth 2.0 / API Keys

# API key in header (server-to-server)
GET /api/v1/data
X-API-Key: sk_live_abc123def456

# OAuth 2.0 scopes
Authorization: Bearer <token>
# Token includes scopes: ["users:read", "orders:write"]

OpenAPI Specification

openapi: 3.1.0
info:
  title: MyApp API
  version: 1.0.0
paths:
  /api/v1/users:
    get:
      summary: List users
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1, minimum: 1 }
        - name: limit
          in: query
          schema: { type: integer, default: 20, minimum: 1, maximum: 100 }
      responses:
        "200":
          description: Paginated list of users
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserListResponse"
components:
  schemas:
    User:
      type: object
      required: [id, name, email]
      properties:
        id: { type: string, example: "usr_abc123" }
        name: { type: string, example: "Alice" }
        email: { type: string, format: email }
        created_at: { type: string, format: date-time }

SDK Generation

# Generate TypeScript client
npx @openapitools/openapi-generator-cli generate \
  -i openapi.yaml -g typescript-fetch -o sdk/typescript

# Generate Python client
openapi-generator generate -i openapi.yaml -g python -o sdk/python

Anti-Patterns

Anti-Pattern Correct Approach
Verbs in URLs (/getUsers) Use HTTP methods with noun resources
Inconsistent naming (userId vs user_id) Pick one convention (snake_case recommended)
Returning 200 for errors Use proper HTTP status codes
No pagination on list endpoints Always paginate, default limit 20
Leaking stack traces in errors Generic message + error code, log details server-side
No rate limiting Implement per-tier rate limits from day one
API keys in query strings Use headers (Authorization or X-API-Key)
No versioning Version from v1, plan for breaking changes
Nested resources > 2 levels deep Flatten: /orders/{id} not /users/{id}/orders/{oid}/items

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.