API-First Architecture
Design the API first. Build everything else against it.
API-first means treating the API as the product, not an afterthought. Contract defined before code. Versioned. Documented. Mocked early so frontend and backend build in parallel. The spec becomes the source of truth every consumer depends on.
openapi.yaml
v1.4.2openapi: 3.1.0
info:
title: Crescentek API
version: 1.4.2
paths:
/products/{id}:
get:
summary: Get product by ID
parameters:
- name: id
in: path
required: true
responses:
'200':
content: application/json
schema: Product
The contract
serving 5 consumers
Consumers · GET /products/sku-047
Web app
Full payload · auth
200OK
101ms
iOS app
Compact payload
200OK
—
Partner API
OAuth · scope:read
200OK
—
Admin tool
Admin · extended fields
200OK
—
Webhook consumer
Event-triggered
200OK
—
One contract · five consumers · all independently versionable · spec auto-generates docs, clients, tests.
The use case
When API-first is the right mindset.
Multi-client products
Web + iOS + Android + partner integrations. If ≥ 2 clients consume your data, API-first saves you from writing the same endpoint three different ways.
Partner / B2B integrations
Third-parties depend on your API. Breaking changes hurt their business. API-first forces versioning discipline and explicit deprecation paths.
Large engineering orgs
Frontend and backend teams work in parallel without blocking each other. Spec defined early; frontend mocks + backend stubs; integration at the end.
Microservices / service mesh
Services talk to each other via APIs. Each service publishes its contract; consumers code against the contract, not the implementation.
Publicly-documented APIs
If customers or developers outside your org use your API, the spec is the product. Docs, SDKs, changelogs all flow from it.
Enterprise compliance needs
Audit trails for API changes, formal deprecation windows, schema-driven validation. All easier when the spec is the source of truth.
The workflow
Design-first, then code. The 6-step workflow.
01
Define the contract
OpenAPI 3.1 YAML, written collaboratively. Paths, methods, request/response shapes, auth schemes, error patterns — all before a line of implementation code.
02
Mock the API
Stoplight Prism or Mockoon spin up a fake server from the spec. Frontend starts building against real-looking responses within hours of the spec being written.
03
Generate clients + docs
openapi-generator produces TypeScript/Swift/Kotlin/Python clients from the spec. Redoc or Stoplight generates hosted docs. One source, many outputs.
04
Implement backend against spec
Backend code must match the contract. Libraries (FastAPI, NestJS) validate requests and responses against the schema — drift fails tests.
05
Contract testing
Pact or schemathesis run the generated client against the live backend. Any drift between spec and reality fails CI before reaching production.
06
Version + evolve
Breaking changes go in /v2. Old endpoints stay working for a documented deprecation window. Partners and mobile apps never surprise-break.
API design patterns we use
Patterns that age well.
These aren't theoretical — each one solves a real pain we've hit mid-project. Include from day 1, not as v2 refactors.
Consistent error envelope
All errors return { code, message, details, request_id }. Clients handle errors uniformly. No one guessing if the API uses `error` or `err` or `errors` or just a plain string.
Pagination via cursor
Offset pagination breaks when data changes mid-paginate. Cursor-based (next_cursor in response, pass as query param) is reliable and O(1).
Idempotency keys on mutations
POST requests include Idempotency-Key header. Retries are safe; no double-charges. Stripe's model — battle-tested.
Field selection (sparse responses)
Consumer requests ?fields=id,name,price rather than full payloads. Mobile gets compact responses; admin UI gets everything. Same endpoint.
HATEOAS-lite with explicit links
Include _links in responses pointing to related resources. Full HATEOAS is over-engineered; adding key navigation links is pragmatic.
Webhook + polling hybrid
Offer both webhooks (push) and polling (pull) for async events. Partners who can't receive webhooks can poll /events?since=cursor.
Versioned headers, not URL paths
Accept: application/vnd.api.v2+json lets you deprecate gracefully without changing URLs. Cleaner than /v1/ /v2/ URL forks.
Rate-limit headers always on
Every response includes X-RateLimit-Limit, Remaining, Reset. Consumers can back off gracefully; less DDoS accidents.
Don't force it
When API-first is overkill.
Single-client web app
Next.js server components calling the database directly. No API to design if only one frontend consumes the data. tRPC or server actions fit better.
Prototypes and MVPs
You don't know the shape yet. Writing a spec before the product exists slows you down. Move to API-first after product-market fit, not before.
Internal tooling with a single consumer
One admin dashboard, built by the same team that built the backend. The contract is the code; API-first adds overhead without payoff.
Real-time apps (WebSockets/gRPC)
OpenAPI shines for request-response. Real-time streaming APIs want AsyncAPI or gRPC's protobuf. Different tooling, different mindset.
Frequently asked
API-first questions.
REST/OpenAPI for public APIs, partner integrations, and most backend work. GraphQL when you have multiple clients with wildly different data needs and a strong frontend team. Both can be API-first; the contract format differs (OpenAPI vs SDL).
Building something with multiple consumers?
Share the use case — web + mobile + partners — and we'll help you design the API contract and architecture from day 1, before it becomes expensive to change.
