Build or update the BlueBubbles external channel plugin for Moltbot (extension package, REST...
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
Cursor-Based (Recommended for Large/Real-Time Datasets)
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.