From consulting-skills
Design ergonomic, consistent REST and HTTP APIs that work well with OpenAPI and code generators. Use this skill when designing an API, writing an OpenAPI spec, reviewing API endpoints, naming resources, choosing HTTP methods, structuring request/response bodies, designing pagination, or planning API versioning. Also triggers for: "what should this endpoint look like", "review my API", "write the OpenAPI spec", "REST design", "API contract", "resource naming", "error response format", or "how should I paginate this". Do NOT use for GraphQL schema design, gRPC protobuf definitions, database schema design (use data-table-design), or general backend architecture (use architecture-decision).
How this skill is triggered — by the user, by Claude, or both
Slash command
/consulting-skills:api-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Design REST and HTTP APIs that are consistent, predictable, and generate clean client code through OpenAPI tooling. Good APIs are obvious to consume, hard to misuse, and survive multiple generations of clients.
Design REST and HTTP APIs that are consistent, predictable, and generate clean client code through OpenAPI tooling. Good APIs are obvious to consume, hard to misuse, and survive multiple generations of clients.
Gather from the user (skip what's already provided):
Follow the conventions in references/api-conventions.md. Key principles:
Resources, not actions. URLs identify nouns, not verbs. HTTP methods express the action.
GOOD: POST /orders (create an order)
BAD: POST /createOrder
Plural nouns for collections. Singular for singletons.
GET /users → list users
GET /users/{id} → get one user
POST /users → create a user
GET /users/me → current user (singleton)
Consistent naming. Use kebab-case for URL paths, camelCase for JSON fields, UPPER_SNAKE_CASE for enum values. Never mix conventions within an API.
Flat over nested. Limit URL nesting to one level of sub-resources. Beyond that, promote to top-level with a filter.
GOOD: GET /orders/{id}/items
AVOID: GET /users/{uid}/orders/{oid}/items/{iid}/adjustments
USE: GET /adjustments?itemId={iid}
Be explicit about types. Every field should have an unambiguous type in the OpenAPI spec. Avoid object without properties. Avoid string for dates — use format: date-time.
Envelope responses consistently. Choose one pattern and use it everywhere:
{
"data": { ... },
"meta": { "requestId": "abc-123" }
}
Or return the resource directly (simpler, works well with code generators):
{
"id": "usr_123",
"name": "Ada Lovelace",
"email": "[email protected]"
}
Pick one. Do not mix.
Use typed IDs. Prefix IDs with the resource type for debuggability: usr_123, ord_456, inv_789. Use string type in OpenAPI, not integer — this future-proofs against ID format changes.
Pagination. Use cursor-based pagination for large or real-time datasets. Offset-based for small, stable datasets.
GET /orders?cursor=eyJpZCI6MTIzfQ&limit=25
Response:
{
"data": [...],
"pagination": {
"nextCursor": "eyJpZCI6MTQ4fQ",
"hasMore": true
}
}
Filtering and sorting. Use query parameters. Keep filter syntax simple — avoid inventing a query language.
GET /orders?status=SHIPPED&createdAfter=2025-01-01&sort=-createdAt
Use a consistent error shape across the entire API. Include enough information for the client to programmatically handle the error AND for a developer to debug it.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The request body contains invalid fields.",
"details": [
{
"field": "email",
"issue": "Must be a valid email address.",
"value": "not-an-email"
}
]
}
}
Rules:
code is a stable, machine-readable string (not the HTTP status code)message is human-readable, safe to show in UIdetails is an array for field-level errorsThe API should generate a clean, complete OpenAPI spec. Apply these rules:
operationId. Use camelCase, format as verbNoun: listOrders, getUser, createInvoice. These become method names in generated SDKs.$ref to shared components. Never inline complex objects.enum for constrained strings. Status fields, type fields, category fields — define them as enums so generated code gets type safety.example values. Every schema property should have an example. This powers documentation and mock servers.required explicitly. List required fields. Do not rely on consumers guessing.CreateUser (all required fields) vs UpdateUser (all optional, patch semantics). Do not reuse the same schema for both.Choose one:
| Strategy | When to Use | Tradeoff |
|---|---|---|
URL prefix (/v1/...) | Public APIs, multiple major versions coexist | Simple to understand, harder to maintain multiple versions |
Header (API-Version: 2025-03-01) | APIs that evolve incrementally | Cleaner URLs, more complex routing |
| No versioning | Internal APIs with lockstep deployment | Simplest, but breaks if consumers can't update in sync |
Default recommendation: URL prefix for external APIs, header-based date versioning for APIs that evolve frequently, no versioning for internal microservice APIs deployed together.
Before finalizing, verify:
operationId values$refDeliver one or more of:
npx claudepluginhub baufest/skills --plugin consulting-skillsDesigns consistent, evolvable REST APIs with correct resource naming, HTTP methods, status codes, versioning, pagination, and error format.
Provides RESTful API design standards covering resource naming, HTTP methods, status codes, pagination, versioning, and error response formats.
Designs consistent RESTful APIs covering conventions, HTTP methods, naming, versioning strategies, response formats, status codes, error handling, and pagination patterns.