How this skill is triggered — by the user, by Claude, or both
Slash command
/backend-engineer:api-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Design API contracts as long-term commitments. A consumer integrates once and depends on your shape for years — the contract is the product, not the implementation behind it. Every rule below protects that commitment.
Design API contracts as long-term commitments. A consumer integrates once and depends on your shape for years — the contract is the product, not the implementation behind it. Every rule below protects that commitment.
For authentication and authorization rules, defer to the secure-by-default skill — do not re-teach auth here.
Model endpoints around resources (nouns) addressed by the HTTP verb, not around actions.
GET /users/42 — fetch a user. DELETE /orders/99 — delete an order. POST /orders — create.POST /getUser, POST /deleteOrder, GET /createOrder. The verb lives in the method, never in the path./users, /orders. Sub-resources nest: /users/42/orders./orders/99/refund) is the rare exception — reach for it only when no resource models the state change.The HTTP method already encodes intent. Verb endpoints duplicate that, break caching and proxies, and make the surface unpredictable.
GET, PUT, and DELETE are naturally idempotent: repeating them yields the same end state. POST is not — retry it and you create two orders.
For any POST that must be retry-safe (payments, order creation, anything that moves money or state), require an idempotency key:
Idempotency-Key: <uuid>.Without it, every dropped response on a POST is a potential duplicate.
Always paginate list endpoints, and always return pagination metadata (e.g. next_cursor, has_more, optionally a count) so the client knows how to continue.
Prefer cursor-based pagination over offset/page-number at scale:
?page=3&size=20 → OFFSET 40) breaks under concurrent writes: a row inserted or deleted between page loads shifts the window, so the client skips or repeats records.?limit=20&cursor=eyJpZCI6MTQwfQ. New writes do not shift earlier pages.Treat the published contract as a long-term commitment:
/v2/... or Accept: application/vnd.api+json;version=2).Every error response — across every endpoint — shares one schema:
status — the HTTP status code.code — a stable, machine-readable error code (VALIDATION_FAILED, not a prose string).message — a human-readable summary.errors — optional field-level validation details.request_id — a request/correlation id for tracing and support.Rules:
code and request_id.Use the status line to communicate outcome. Never return 200 for a failure.
| Code | Meaning |
|---|---|
| 200 | OK — successful GET/PUT/PATCH with a body |
| 201 | Created — resource created (return it / its Location) |
| 204 | No Content — success with no body (e.g. DELETE) |
| 400 | Bad Request — malformed syntax / unparseable |
| 401 | Unauthorized — missing or invalid authentication |
| 403 | Forbidden — authenticated but not allowed |
| 404 | Not Found — resource does not exist |
| 409 | Conflict — state conflict (duplicate, version clash) |
| 422 | Unprocessable Entity — well-formed but semantically invalid |
| 429 | Too Many Requests — rate limited |
| 500 | Internal Server Error — unexpected server fault |
Pick REST unless the heterogeneity pain is real and present. Do not adopt GraphQL for a single, stable client.
A language-agnostic error response for a failed POST /users with two invalid fields:
{
"status": 422,
"code": "VALIDATION_FAILED",
"message": "The request could not be processed.",
"errors": [
{ "field": "email", "code": "INVALID_FORMAT", "message": "Email is not a valid address." },
{ "field": "age", "code": "OUT_OF_RANGE", "message": "Age must be 18 or older." }
],
"request_id": "req_01HZX9F3KQ2"
}
Every endpoint returns this exact shape on error — clients write one parser, not one per route.
/getUser, /deleteOrder. The method already carries the verb; keep paths as nouns.POST for everything with no idempotency — every retried request risks a duplicate. Use the right verb; add an idempotency key for unsafe retries.200 for errors — clients and proxies treat it as success; failures vanish into the body. Use the right status code.npx claudepluginhub shoto290/shoto --plugin backend-engineerGuides test-driven development for Django applications using pytest-django, factory_boy, and Django REST Framework. Covers red-green-refactor workflow, conftest fixtures, and coverage reporting.