Overview
An API is a promise: once a consumer depends on it, every change is a negotiation. API design is the craft of making that promise clear (resources, verbs, errors), choosing the right style for the job (REST, gRPC, GraphQL, events), and evolving it without breakage. It is the seam where application architecture meets system design — the contract your hexagon's adapters expose and your consumers build against.
Key points
- REST as resource modeling: nouns not verbs, HTTP methods with their real semantics (idempotency of PUT/DELETE, safety of GET), status codes used honestly, pagination (cursor over offset), filtering, consistent error shape (RFC 9457 problem details).
- The style trade: REST (ubiquitous, cacheable, loose), gRPC (typed contracts, streaming, fast — internal service-to-service sweet spot), GraphQL (client-shaped queries — pays off with many heterogeneous clients, costs server complexity and caching), webhooks/events for push.
- Contract-first: OpenAPI / protobuf schema as the source of truth — generated clients/servers, contract tests in CI, and consumer-driven contracts to keep providers honest. The doc formats and the generate-doc-from-code vs generate-code-from-doc trade get their own note: API documentation.
- Versioning is a last resort: additive, backward-compatible evolution first (tolerant reader: ignore unknown fields); when breaking is unavoidable — explicit versions, deprecation windows, sunset headers. Never silently change semantics.
- Operational surface: authn/authz (OAuth2/OIDC scopes), rate limiting and quotas, idempotency keys for unsafe retries, timeouts documented as part of the contract.
- Design for the consumer: the best APIs are designed from the caller's use cases inward, not the database schema outward.
- To explore: API gateways vs. BFFs, hypermedia (when HATEOAS earns its keep), gRPC error model.
Practice
- Build your own URL shortener (source) — a deliberately small surface: resource modeling, honest status codes, and idempotent creation, done right end to end.
- Contract-first kata (source) — write the OpenAPI document before the code for a small service, generate client and server stubs, and put a contract test in CI.
- REST endpoint, re-served as gRPC (source) — port one of your endpoints to a protobuf contract with server streaming; the style trade stops being theoretical.
- Build your own web server (source) — Crickett's challenge: HTTP from the socket up, where method semantics, status codes, and headers earn their meaning.
- Build a load balancer & rate limiter (source) —
the operational surface (
429,Retry-After, rate-limit headers) as running code instead of a paragraph in the docs. - RealWorld full-stack build (source) — implementing a fixed public REST contract exactly, pagination and error semantics included, with someone else's tests as the judge.
Related
- API documentation — the contract artifacts themselves: OpenAPI,
AsyncAPI,
.proto, SDL, and which direction the code flows. - Microservice architecture — service independence lives or dies on contract discipline.
- Hexagonal architecture — the API is a driving adapter's face.
- Networking fundamentals — HTTP semantics and caching come from there.
- Asynchronous and distributed system patterns — the event-based alternative to request/response.
- WebSockets & bidirectional protocols — the push side of the contract surface.
- Rate limiting & idempotency — the operational guardrails of the contract, in depth.