Help us improve
Share bugs, ideas, or general feedback.
From grimoire
Designs HTTP API endpoints following REST conventions: resource naming, HTTP methods, status codes, versioning, and response shapes.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireHow this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:design-restful-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Model resources as nouns, use HTTP verbs for actions, and return consistent status codes.
Designs consistent, evolvable REST APIs with correct resource naming, HTTP methods, status codes, versioning, pagination, and error format.
Guides RESTful API design and implementation: resource naming, HTTP methods, URL patterns, error responses, versioning, and core principles.
Designs consistent RESTful APIs covering conventions, HTTP methods, naming, versioning strategies, response formats, status codes, error handling, and pagination patterns.
Share bugs, ideas, or general feedback.
Model resources as nouns, use HTTP verbs for actions, and return consistent status codes.
Adopted by: GitHub, Stripe, Twilio, Google (Cloud APIs), AWS, Shopify, Salesforce — REST is the dominant public API style. Stripe's API (launched 2011, unchanged v1 surface) is the industry benchmark for developer experience. Impact: REST APIs outlive their implementations. Stripe's v1 API is still compatible 13+ years later because HTTP semantics don't change. Teams with consistent REST conventions resolve API design disputes in minutes instead of days — the RFC is the tie-breaker. Why best: GraphQL requires schema introspection tooling and shifts query complexity to clients. RPC (gRPC, JSON-RPC) couples clients to server method signatures. REST uses HTTP semantics that every HTTP client already understands — no SDK required to consume a well-designed REST API.
Sources: Fielding dissertation (ics.uci.edu/~fielding/pubs/dissertation/), IETF RFC 9110, Stripe API reference, Google Cloud API Design Guide
Resources are things, not actions:
# Bad — verb in URL
GET /getUser/123
POST /createOrder
DELETE /deleteItem?id=5
# Good — noun resources
GET /users/123
POST /orders
DELETE /items/5
Nest sub-resources when they only exist in the context of a parent:
GET /orders/42/items # items belonging to order 42
POST /orders/42/items # add item to order 42
DELETE /orders/42/items/7 # remove specific item
Don't nest deeper than 2 levels — URLs become unreadable and brittle.
| Verb | Semantics | Idempotent? | Body? |
|---|---|---|---|
| GET | Read — never mutate | Yes | No |
| POST | Create / non-idempotent action | No | Yes |
| PUT | Full replace | Yes | Yes |
| PATCH | Partial update | No | Yes |
| DELETE | Remove | Yes | No |
Use POST for actions that don't fit CRUD: /orders/42/cancel, /users/me/verify-email.
Always return the semantically correct code — clients branch on these:
| Code | When |
|---|---|
| 200 | Successful GET, PUT, PATCH |
| 201 | Successful POST that created a resource — include Location header |
| 204 | Successful DELETE or action with no response body |
| 400 | Malformed request (syntax, missing required field) |
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
| 404 | Resource not found |
| 409 | Conflict (duplicate create, stale update) |
| 422 | Validation failed (well-formed but semantically invalid) |
| 429 | Rate limited — include Retry-After header |
| 500 | Server error — never leak stack traces |
/v1/users
/v2/users
Never version via header — it's invisible in browser logs, curl output, and URLs shared
in Slack. Maintain old versions until all clients have migrated; announce deprecation
with a sunset date in the Sunset response header (RFC 8594).
Pick one shape and never deviate:
// Success (single resource)
{ "data": { "id": "usr_123", "email": "..." } }
// Success (collection)
{ "data": [...], "meta": { "total": 500, "page": 2, "per_page": 20 } }
// Error
{ "error": { "code": "validation_failed", "message": "...", "fields": {...} } }
Never return unbounded lists — a table with 10M rows will break clients and servers.
GET /users?page=2&per_page=50 # offset pagination — simple, allows jumping
GET /users?cursor=eyJpZCI6MTAwfQ== # cursor pagination — consistent under writes
Cursor pagination is preferred for large datasets or frequently-changing collections.
Idempotency-Key) for payment or critical creation endpoints/cancel is acceptable as a POST target, not as a GETcode field — human-readable message alone forces string parsingusr_abc123) that allow backend changesUsing 200 for errors. { "success": false } with HTTP 200 breaks every HTTP client
library's error handling. Use the right 4xx/5xx code.
Returning different shapes for success and error. Clients handle one envelope, not two.
Skipping versioning. "We'll add versioning later" means the first breaking change
is a crisis. Start with /v1/ on day one.
Deeply nested routes. /companies/1/departments/2/teams/3/members/4 — use
/members/4 and include parent IDs in the response body.