plugins/api-design/skills/api-design/SKILL.md
Use when designing REST or GraphQL APIs, choosing URL structures, error formats, pagination strategies, or authentication patterns. Covers HTTP conventions, versioning, OpenAPI, and rate limiting.
npx skillsauth add sagargupta16/claude-skills api-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Task | Approach |
|------|----------|
| URL structure | Nouns for resources, HTTP verbs for actions |
| Versioning | URL prefix (/api/v1/) for most projects |
| Errors | RFC 9457 problem details format |
| Pagination | Cursor-based for large datasets, offset for small |
| Auth | JWT bearer tokens or API keys depending on use case |
| Docs | OpenAPI 3.1 spec, auto-generated where possible |
GET /api/v1/users # List users
POST /api/v1/users # Create user
GET /api/v1/users/{id} # Get user by ID
PUT /api/v1/users/{id} # Replace user
PATCH /api/v1/users/{id} # Partial update
DELETE /api/v1/users/{id} # Delete user
GET /api/v1/users/{id}/orders # Nested resource
POST /api/v1/users/{id}/orders # Create nested resource
Rules:
/users not /user/order-items/users/{id}/activate not /activateUserGET /api/v1/users?status=active&role=admin # Filter
GET /api/v1/users?sort=-created_at,name # Sort (- for desc)
GET /api/v1/users?search=john # Search
GET /api/v1/users?fields=id,name,email # Sparse fields
| Method | Purpose | Idempotent | Body | |--------|---------|-----------|------| | GET | Read resource(s) | Yes | No | | POST | Create resource | No | Yes | | PUT | Replace resource | Yes | Yes | | PATCH | Partial update | No | Yes | | DELETE | Remove resource | Yes | No |
| Code | When to Use | |------|-------------| | 200 | Successful GET, PUT, PATCH, DELETE | | 201 | Successful POST (resource created) | | 204 | Successful DELETE with no body | | 400 | Invalid request body or params | | 401 | Missing or invalid authentication | | 403 | Authenticated but not authorized | | 404 | Resource not found | | 409 | Conflict (duplicate, state violation) | | 422 | Valid JSON but failed validation | | 429 | Rate limit exceeded | | 500 | Unhandled server error |
Use RFC 9457 Problem Details:
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 422,
"detail": "Email address is already in use",
"instance": "/api/v1/users",
"errors": [
{
"field": "email",
"message": "Email '[email protected]' is already registered"
}
]
}
Rules:
status, title, and detailerrors array for field-level validation failuresFastAPI:
from fastapi import HTTPException
from fastapi.responses import JSONResponse
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content={
"type": f"https://api.example.com/errors/{exc.status_code}",
"title": exc.detail,
"status": exc.status_code,
},
)
Express:
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({
type: `https://api.example.com/errors/${status}`,
title: err.message,
status,
});
});
| Strategy | Format | When to Use |
|----------|--------|-------------|
| URL prefix | /api/v1/users | Default choice -- simple, visible, cacheable |
| Header | Accept: application/vnd.api+json;version=2 | When URL stability matters |
| Query param | /api/users?version=2 | Avoid -- poor caching |
When to bump versions:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTAwfQ==",
"has_more": true
}
}
Request: GET /api/v1/users?cursor=eyJpZCI6MTAwfQ==&limit=25
Advantages: consistent results, no skipped/duplicated items on insert/delete.
{
"data": [...],
"pagination": {
"total": 150,
"page": 2,
"per_page": 25,
"total_pages": 6
}
}
Request: GET /api/v1/users?page=2&per_page=25
Always enforce a max page size server-side:
@router.get("/users")
async def list_users(page: int = 1, per_page: int = Query(default=25, le=100)):
...
| Pattern | Use Case | Implementation |
|---------|----------|---------------|
| JWT Bearer | SPA, mobile apps | Authorization: Bearer <token> |
| API Key | Server-to-server, third-party integrations | X-API-Key: <key> or query param |
| Session cookie | Traditional web apps | Set-Cookie: session=<id> |
| OAuth 2.0 | Third-party login | Authorization code flow |
JWT structure:
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"sub": "user_123", "exp": 1700000000, "role": "admin"}
Rules:
Response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1700000060
Retry-After: 30
Common limits:
Return 429 Too Many Requests with Retry-After header.
Auto-generate where possible:
/docs (Swagger UI) and /redocswagger-jsdoc + swagger-ui-expressswag annotations@nestjs/swagger decoratorsMinimum spec:
openapi: "3.1.0"
info:
title: API Name
version: "1.0.0"
paths:
/api/v1/users:
get:
summary: List users
parameters:
- name: page
in: query
schema: { type: integer, default: 1 }
responses:
"200":
description: User list
| Don't | Do Instead |
|-------|-----------|
| Use verbs in URLs (/getUsers) | Use HTTP methods on noun resources |
| Return 200 for errors with error body | Use proper HTTP status codes |
| Expose database IDs directly | Use UUIDs or opaque IDs |
| Return different error formats per endpoint | Use consistent error schema everywhere |
| Paginate without a max limit | Always enforce server-side max page size |
| Version every minor change | Only version on breaking changes |
| Return full nested objects always | Support sparse fields or use separate endpoints |
| Accept * CORS in production | Whitelist specific origins from env vars |
testing
Use when the user asks to audit a session for uncaptured learnings. Activates on "audit this session", "session audit", "what did we miss", "end of session check", or "/starter-session-audit". Scans the conversation for corrections, preferences, decisions, and new context, then proposes where to save each.
testing
Use when setting up new repositories, auditing existing ones, or preparing repos for public visibility. Generates .gitignore, .env.example, README, and LICENSE files. Detects committed secrets and flags security issues.
tools
Use when triaging open Renovate PRs across your own repos into merge / close / defer. Activates on "renovate triage", "review dep PRs", "monthly deps", or on the 1st of a month if deps are grouped monthly.
development
Use when restructuring code without changing behavior -- extracting functions, renaming, moving files, reducing duplication, migrating between patterns (JS to TS, CJS to ESM), or addressing code smells. Covers safe refactoring workflows for any language.