plugins/backend-toolkit/skills/api-contract/SKILL.md
Define a schema-first API contract — standardized error envelope (RFC 9457), pagination, status codes, consistent JSON shapes. Use when establishing API conventions, before multiple teams consume an API, or when error responses are inconsistent. Not for choosing the protocol or modeling resources (use api-design) or for runtime input parsing at the boundary (use data-validation).
npx skillsauth add jaykim88/claude-ai-engineering api-contractInstall 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.
Establish one consistent contract — error envelope, pagination, status codes, JSON shape — so clients can be written against a stable, extensible API and breaking changes are caught before they ship.
Universal — RFC 9457 problem+json, cursor-vs-offset pagination, status-code semantics, and "object-as-root" are HTTP/spec-level conventions that apply to any backend.
Standardize the error envelope (RFC 9457 application/problem+json)
{ type, title, status, detail, instance } + domain extensionsAlways return a top-level JSON object (never a bare array)
{ "items": [...], "nextCursor": "..." } not [...]Pick a pagination strategy and apply it everywhere
Use status codes by their HTTP semantics
200/201/204 success; 400 validation; 401 unauthenticated; 403 unauthorized; 404 not found; 409 conflict; 422 semantic validation; 429 rate-limited; 5xx server4xx = client must change request; 5xx = client may retry429 must include Retry-After (seconds or HTTP-date) — without it clients retry blindly and amplify the storm; rate-limit metadata in RateLimit-* headers (IETF draft) helps well-behaved clients self-throttle4b. Idempotency header for non-idempotent endpoints
Idempotency-Key: <uuid> header on POSTs that create/charge — store key→result so a retried request returns the original result, not a duplicate effect (see resilience-patterns)4c. Optimistic concurrency via ETag / If-Match
ETag: "<version>"; write endpoint requires If-Match: "<version>" and returns 412 Precondition Failed on mismatchSchema-first: generate, don't hand-write
Validate (validation loop)
| ❌ Anti-pattern | ✅ Correct |
|---|---|
| Different error shape per endpoint | One RFC 9457 envelope everywhere |
| Bare array response ([...]) | { items: [...], nextCursor } object |
| 200 OK with { error: "..." } in body | Correct status code (400/409/etc.) |
| Offset pagination on a high-write table | Cursor-based pagination |
| Hand-maintained client types drifting from server | Generate both from one OpenAPI schema |
| 429 with no Retry-After (clients retry blindly) | Retry-After (+ RateLimit-* headers) on every 429 |
| POST that creates a charge with no idempotency support | Accept Idempotency-Key header; store key→result |
| Last-write-wins on concurrent updates (silent loss) | ETag on read + If-Match on write → 412 on mismatch |
feat(api): add <endpoint> to contract / fix(api): standardize error envelope@nestjs/swagger generates OpenAPI from decorators (code-first)ExceptionFilter mapping to RFC 9457spectral lint openapi.jsonswaggo/swag or hand-written OpenAPI; problem+json middlewareapi-design — choose protocol/resources first, then formalize the contract heredata-validation — the contract's request schema drives boundary validationapplication/problem+json, RFC 9457) and always return a top-level JSON object (never a bare array) so the contract can extend without breaking clients.development
Design webhooks correctly on both sides — sending (HMAC signing, retries with backoff, at-least-once) and receiving (verify signature on raw body, enqueue + 200 fast, dedupe on event id). Use when adding webhook delivery or consuming a provider's webhooks. Not for internal service-to-service events (use async-messaging) or general outbound-call retry policy (use resilience-patterns).
testing
Use transactions and isolation levels correctly — keep them short, no network calls inside, explicit isolation, retry on serialization conflicts, and choose optimistic vs pessimistic locking. Use when a write spans multiple tables, when concurrent updates corrupt data, or when designing money/inventory flows. Not for cross-service event delivery (use async-messaging Outbox) or schema-level constraints (use schema-design).
development
Backend testing pyramid — unit for pure logic, integration against a real DB (Testcontainers), and consumer-driven contract testing (Pact) for service boundaries. Use before a feature, after a bug fix, or when services break each other on deploy. Not for load testing (use performance-profiling) or security testing (use backend-security-audit).
data-ai
Design a relational schema — normalize to 3NF then denormalize with justification, choose the right Postgres index type per data shape, enforce constraints at the DB. Use when modeling a new domain, when queries are slow, or before a migration. Not for diagnosing slow queries (use query-optimization) or shipping the change without downtime (use migration-strategy).