Designs durable API contracts across REST, GraphQL, gRPC, tRPC, and AsyncAPI. Use when specifying interfaces, auth, versioning, errors, rate limits, or agent APIs.
Installs into .claude/skills of the current project.
Are you the author of Dev Api Design?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/vasilyu1983-dev-api-design)
---
name: dev-api-design
description: Designs durable API contracts across REST, GraphQL, gRPC, tRPC, and AsyncAPI. Use when specifying interfaces, auth, versioning, errors, rate limits, or agent APIs.
compatibility: Portable core. Works on Claude Code and Codex.
version: "1.2"
last_validated: 2026-07-11
---
# API Design
Use this skill for contract-first API design across REST, GraphQL, gRPC, tRPC, AsyncAPI, and agent-facing interfaces. It owns contract choice, auth boundaries, versioning, errors, pagination, rate limits, idempotency, and validation; it does not replace backend implementation or security review.
## Style Decision Table
| API style | Choose when | Avoid when | Canonical artifact |
|-----------|-------------|------------|--------------------|
| REST + OpenAPI | Public APIs, broad tooling compatibility, cacheable resources | Streaming-first service contracts | OpenAPI 3.x |
| GraphQL | Complex client-driven query shapes, multi-team schema ownership | Simple CRUD, caching is critical, single-team | GraphQL SDL |
| gRPC | Internal services, bidirectional streaming, type-critical boundaries | Public internet consumers, browser-native clients | protobuf |
| tRPC | TypeScript monorepos with shared server+client | Non-TS stacks, public third-party consumers | TypeScript types |
| AsyncAPI + webhooks | Event-driven contracts, pub/sub, push notifications | Synchronous request-reply | AsyncAPI 3.x |
| MCP tool layer | Agent or LLM is the primary consumer | Human-only clients | MCP tool schema |
## Quick Reference
| API style | Load |
|-----------|------|
| REST + OpenAPI | [references/restful-design-patterns.md](references/restful-design-patterns.md), [references/openapi-guide.md](references/openapi-guide.md) |
| GraphQL | [references/graphql-patterns.md](references/graphql-patterns.md) |
| gRPC | [references/grpc-patterns.md](references/grpc-patterns.md) |
| tRPC | [references/trpc-patterns.md](references/trpc-patterns.md) |
| AsyncAPI and webhooks | [references/asyncapi-patterns.md](references/asyncapi-patterns.md), [references/webhook-patterns.md](references/webhook-patterns.md) |
| Core cross-cutting | [references/error-handling-patterns.md](references/error-handling-patterns.md), [references/authentication-patterns.md](references/authentication-patterns.md), [references/pagination-filtering.md](references/pagination-filtering.md), [references/rate-limiting-patterns.md](references/rate-limiting-patterns.md), [references/api-testing-patterns.md](references/api-testing-patterns.md) |
## Contract Review Checklist
Run on every API contract before handoff:
- [ ] Canonical spec artifact exists (OpenAPI, AsyncAPI, protobuf, GraphQL SDL, or MCP schema)
- [ ] Compatibility and versioning model named: evolutionary changes, URL path (`/v1/`), header, or content-type negotiation
- [ ] Deprecation timeline written into the spec or linked doc; `Deprecation` header uses the RFC 9745 `@<unix-seconds>` form, `Sunset` uses RFC 8594
- [ ] Error model: for REST/HTTP, use RFC 9457 Problem Details with stable `type` URI and an application `code` extension when clients need one; for GraphQL, gRPC, and events, define errors in their native contract
- [ ] Auth boundary: which endpoints require which scopes; token type (JWT, opaque); revocation path
- [ ] Pagination: cursor-based for high-cardinality; offset only for small, stable sets
- [ ] Rate limits: either the IETF httpapi `RateLimit` / `RateLimit-Policy` fields or a documented legacy `X-RateLimit-*` triad; 429 includes `Retry-After`. The IETF fields have changed shape between drafts: look up the current revision and RFC status on the IETF datatracker and copy the syntax from that revision, and name the revision in the spec
- [ ] Idempotency: POST/PATCH operations document idempotency key or mark non-idempotent explicitly; whatever the IETF `Idempotency-Key` header's status (check datatracker), the spec must define key scope, retention, and the reused-key response
- [ ] Long-running jobs: 202 + `Location` poll URL; `state` enum with terminal states named
- [ ] Webhooks: HMAC signature; replay protection; `trace_id` on payload
- [ ] Breaking-change detection: oasdiff or equivalent configured in CI
- [ ] Contract tests: Schemathesis (property-based) or Pact (consumer-driven) wired up
## Workflow
1. Choose the API style using the decision table above.
2. Define the canonical contract artifact.
3. Run the contract review checklist.
4. Add contract validation, breaking-change detection, and documentation.
5. Hand off spec, examples, and rollout notes.
### Compatibility evidence by change class
Classify each change before calling the contract compatible:
| Change class | Required evidence |
|--------------|-------------------|
| shape change | schema diff plus generated-client compile or consumer contract test |
| semantic change with stable shape | before/after examples and a named behavioral assertion |
| default, ordering, quota, or timeout change | production-like consumer test and rollout note |
| removal or narrowing | usage evidence, deprecation window, and rollback or compatibility shim |
For provider/consumer deployments, derive the safe order from message direction, changed payload side, and actual consumer tolerance. For a new optional request capability, ship provider support before consumers send it. For response enum/event expansion, or whenever a consumer may reject unknown fields or variants, ship and verify tolerant consumers before the provider emits the new value. For removals, ship consumers first, confirm old-field traffic is gone, then remove the provider behavior. Prove the relevant consumer behavior rather than inferring safety from schema additivity; a green schema diff alone is insufficient evidence for semantic compatibility or deploy order.
## Route Elsewhere
- Backend implementation: [software-backend](../software-backend/SKILL.md)
- AppSec review and auth hardening: [software-security-appsec](../software-security-appsec/SKILL.md)
- System-wide architecture: [software-architecture-design](../software-architecture-design/SKILL.md)
- Rollout and migration sequencing: [dev-workflow-planning](../dev-workflow-planning/SKILL.md)
## Defaults
- Define the contract before implementation or code generation.
- Use RFC 9457 Problem Details for REST/HTTP errors that need a response body; define native error shapes for other API styles.
- Make versioning, deprecation, idempotency, pagination, and rate limits explicit in the spec.
- Prefer OpenAPI 3.x or AsyncAPI as the canonical source for HTTP or event-driven interfaces; pin the newest minor that every validator, linter, and generator in your pipeline supports.
- Treat agent APIs as domain contracts with clear side effects, not thin wrappers around random endpoints.
- Use MCP as the tool-exposure layer when an agent is the primary consumer. Its authorization model builds on OAuth resource-server standards (RFC 9728 Protected Resource Metadata, RFC 8707 Resource Indicators), but session, handshake, and auth requirements change between spec revisions: look up the current revision at modelcontextprotocol.io before designing around sessions or the `initialize` handshake. See [references/llm-agent-api-contracts.md](references/llm-agent-api-contracts.md).
## Versioning Strategy Table
| Approach | When to use | Breaking-change gate |
|----------|-------------|----------------------|
| URL path versioning (`/v2/`) | Public APIs, broad client install base | oasdiff `--fail-on ERR` in CI |
| Header versioning (`API-Version: 2`) | Internal APIs, frequent iteration | oasdiff on each PR |
| Content-type negotiation | Hypermedia or media-type-driven APIs | Manual review + tests |
| Evolutionary (GraphQL, gRPC) | Teams own schema, introspection tools run | GraphQL Inspector / protobuf compatibility |
## Expert Judgment
**Versioning strategy — pick based on who controls the client, not team preference.**
- If you do not control every client (public API, third-party integrators, mobile apps you cannot force-update), publish a version policy and a support window based on observed consumer use and migration lead time; you cannot silently migrate callers.
- If you control every client (internal service mesh, monorepo with generated clients), prefer evolutionary compatibility (additive fields, deprecate-then-remove) over versioning — a new version number is a coordination tax you don't need to pay.
- Dated versions can be useful when each account or request pins behavior independently. If requests omit a version, resolve to a documented pinned default rather than silently upgrading callers, and echo the resolved version in a response header.
- Never let "evolutionary" become an excuse to skip a compatibility gate — GraphQL and gRPC still need CI-enforced schema diffing (GraphQL Inspector, `buf breaking`); "no versioning" is not "no discipline."
Rule: `rules/api/contracts.md` loads this invariant when Claude edits a matching file.
**Breaking-change detection instincts — what schema-diff tools structurally cannot catch:**
The instances below are all applications of Hyrum's Law: with enough consumers of an API, every observable behavior — not just the documented schema — becomes a de facto contract, whether or not you ever promised it. (Same law, applied to schema deploy-sequencing rather than API surface, in [software-database-design/references/migration-strategies.md](../software-database-design/references/migration-strategies.md#hyrums-law-and-the-adds-vs-drops-rule).) That is why a clean schema diff is not proof of compatibility:
- Semantic changes with no shape change: tightening an existing enum's allowed values, narrowing a previously-permissive validation rule, or changing what a field *means* while keeping its type — `oasdiff`/`buf breaking` will report zero diff.
- Behavioral defaults: changing a default sort order, default page size, or default timeout is a breaking change for callers who rely on the default, even though the schema is untouched.
- Cross-field coupling: a field that used to be optional-but-ignored becoming optional-but-enforced (e.g., a previously-cosmetic `region` field now affecting routing).
- Rate-limit and quota tightening: not a contract break in the schema sense, but it breaks production traffic identically to a removed field — treat quota changes with the same deprecation-notice discipline as field removal.
- Error-code reclassification: moving a case from `404` to `410`, or from a generic `code` to a more specific one, breaks clients that pattern-match on the old code even though the Problem Details shape is unchanged.
- Treat automated diffing (oasdiff, GraphQL Inspector, `buf breaking`, Pact) as a floor, not a ceiling — pair it with a changelog review by someone who understands what callers actually depend on.
Hyrum's Law framing adapted from addyosmani/agent-skills (MIT), commit `7676817`, 2026-08-09.
**When GraphQL, gRPC, or event-driven contracts are the wrong choice:**
- GraphQL is wrong for simple CRUD with one client shape, for teams that need HTTP-cache semantics (the GraphQL-over-HTTP spec requires POST support and makes GET optional, so many servers accept only POST; HTTP caching then needs GET plus persisted queries), and for single-team ownership where the query-flexibility payoff is never realized — you inherit N+1 risk, query-complexity DoS surface, and federation tooling cost for no client benefit.
- Native gRPC is a poor default for browser clients, which need gRPC-Web or a compatible transport layer. For public third-party integrators, account for protobuf tooling and transport support; choose REST+OpenAPI when broad HTTP interoperability and discoverability matter more.
- AsyncAPI/event contracts are wrong when the caller needs an immediate, correlated answer to a specific request — forcing request/response workflows through pub/sub adds correlation-ID bookkeeping and timeout ambiguity that plain synchronous HTTP avoids.
- The tell that a style choice was fashion, not fit: nobody on the team can name the specific latency budget, client platform constraint, or multi-team ownership problem the chosen style solves.
## Known Traps
- Picking GraphQL, gRPC, or AsyncAPI for architectural fashion instead of actual client, latency, or interoperability constraints.
- Designing happy-path resources without an explicit idempotency and retry story for duplicated or partial-failure requests.
- Letting auth stay implicit until implementation, producing inconsistent enforcement across endpoints.
- Reusing pagination models that leak internal storage semantics into the public contract.
- Treating webhook or event delivery as reliable push without signature validation, replay protection, or consumer backpressure.
- Generating a spec from code after implementation and calling it contract-first.
- Wrapping arbitrary internal endpoints as agent APIs without stable side-effect, auth, and validation rules.
## Navigation
**Core patterns:**
- [references/restful-design-patterns.md](references/restful-design-patterns.md) — HTTP method semantics, URL structure, idempotency
- [references/pagination-filtering.md](references/pagination-filtering.md) — cursor vs offset, filtering contracts
- [references/error-handling-patterns.md](references/error-handling-patterns.md) — RFC 9457, error taxonomy
- [references/authentication-patterns.md](references/authentication-patterns.md) — OAuth 2.1, JWT, API keys, mTLS
- [references/rate-limiting-patterns.md](references/rate-limiting-patterns.md) — headers, 429 handling, burst strategies
**Style-specific:**
- [references/api-design-best-practices.md](references/api-design-best-practices.md) — cross-style DX and governance
- [references/versioning-strategies.md](references/versioning-strategies.md) — breaking change detection, deprecation
- [references/api-security-checklist.md](references/api-security-checklist.md) — OWASP API Top 10 checklist
- [references/graphql-patterns.md](references/graphql-patterns.md) — schema design, federation, when to adopt a supergraph
- [references/grpc-patterns.md](references/grpc-patterns.md) — protobuf design, streaming, deadlines
- [references/trpc-patterns.md](references/trpc-patterns.md) — TypeScript end-to-end type sharing
- [references/openapi-guide.md](references/openapi-guide.md) — OpenAPI 3.x authoring, linting, bundling
- [references/openapi-32-arazzo-101.md](references/openapi-32-arazzo-101.md) — what OpenAPI 3.2 added over 3.1 (streaming types, QUERY method, OAuth flows) and Arazzo multi-step workflow chains; load when specifying multi-call API sequences or adopting 3.2 features
- [references/asyncapi-patterns.md](references/asyncapi-patterns.md) — event-driven contracts, pub/sub
- [references/webhook-patterns.md](references/webhook-patterns.md) — HMAC signing, replay protection, delivery guarantees
- [references/real-time-api-patterns.md](references/real-time-api-patterns.md) — SSE, WebSocket, long-polling tradeoffs
- [references/api-testing-patterns.md](references/api-testing-patterns.md) — contract tests, property-based testing, Schemathesis
- [references/llm-agent-api-contracts.md](references/llm-agent-api-contracts.md) — MCP integration, AX design, agent-first patterns, LLM-consumer rules (summary-stat endpoints, language-first search, payload pruning as accuracy control)
**Assets and templates:**
- [assets/openapi-template.yaml](assets/openapi-template.yaml)
- [assets/spectral-ruleset.yaml](assets/spectral-ruleset.yaml) — custom Spectral lint ruleset for OpenAPI
- [assets/oasdiff-ci.yml](assets/oasdiff-ci.yml) — CI workflow for oasdiff breaking-change detection
- Full-stack framework scaffolds (FastAPI, Express, Django REST, Spring Boot) are implementation, not contract design — moved to `software-backend`: `assets/python/template-python-fastapi-sqlalchemy.md`, `assets/nodejs-express/template-nodejs-express.md`, `assets/python-django/template-python-django-rest.md`, `assets/java/template-java-spring-boot.md`
- [assets/cross-platform/api-patterns-universal.md](assets/cross-platform/api-patterns-universal.md)
- [assets/cross-platform/template-api-governance.md](assets/cross-platform/template-api-governance.md)
- [assets/cross-platform/template-api-design-review-checklist.md](assets/cross-platform/template-api-design-review-checklist.md)
- [assets/cross-platform/template-api-error-model.md](assets/cross-platform/template-api-error-model.md)
- [data/sources.json](data/sources.json)
**Related skills:**
- [software-backend](../software-backend/SKILL.md), [software-security-appsec](../software-security-appsec/SKILL.md), [data-sql-optimization](../data-sql-optimization/SKILL.md), [qa-testing-strategy](../qa-testing-strategy/SKILL.md), [qa-observability](../qa-observability/SKILL.md), [docs-codebase](../docs-codebase/SKILL.md), [docs-ai-prd](../docs-ai-prd/SKILL.md), [dev-workflow-planning](../dev-workflow-planning/SKILL.md), [software-architecture-design](../software-architecture-design/SKILL.md)
## Learnings Loop
When prior decisions or pitfalls are relevant, consult `learnings.consolidated.md` if present; use `learnings.md` only for needed history or as the available fallback. Otherwise skip both.
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to `learnings.md` via `agents-skills-feedback-loop/scripts/append_learning.py`. Do not modify `SKILL.md` itself.