In traditional development, the backend team builds endpoints, and the frontend team waits. When the backend is "ready," integration begins, and surprises emerge. Field names don't match. A required field was forgotten. The timeline slips. APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.-first development flips this by agreeing on the contract first, so both teams can move in parallel.
Traditional vs APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.-first workflow
The difference is when you make decisions about data shape and endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users. structure.
Traditional:
1. Backend builds endpoints (guessing what frontend needs)
2. Frontend waits or builds with hardcoded data
3. Both teams integrate → find mismatches
4. Fix and delay
API-first:
1. Both teams design the API spec together
2. Backend implements to the spec
3. Frontend builds against a mock server from the spec
4. Integration works because both sides followed the same contractThe week you spend designing the spec upfront saves multiple weeks of integration pain later.
Designing an OpenAPIWhat is openapi?A standard format for describing REST APIs - their endpoints, parameters, and response shapes. Tools can generate documentation and client libraries from it automatically. spec
OpenAPI is YAMLWhat is yaml?A human-readable text format used for configuration files, including Docker Compose and GitHub Actions workflows. (or JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it.) that describes every endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users., parameter, request body, and response shape. It is machine-readable, which means tools can generate mockWhat is mock?A fake replacement for a real dependency in tests that records how it was called so you can verify interactions. servers, client SDKs, and documentation from it automatically.
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: stringThe APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.-first workflow in practice
Step 1: design the spec together
Gather frontend, backend, mobile, and product in a room (or a shared document). Design the endpoints collaboratively. What data does each page need? What are the response shapes? What do errors look like?
Step 2: generate a mockWhat is mock?A fake replacement for a real dependency in tests that records how it was called so you can verify interactions. server
Once the spec is agreed upon, spin up a mock server in one command. Frontend teams can hit real HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. endpoints that return example data from the spec, no backend code required yet.
npx @stoplight/prism-cli mock api-spec.yaml
# Mock server running at http://localhost:4010Step 3: develop in parallel
Backend fills in the implementation. Frontend builds the UI against the mock. Both teams move at full speed.
Step 4: swap mock for real
When the backend is ready, update the base URL. The integration should work because both sides followed the same contract.
// Frontend code works with mock AND real API
const API_URL = process.env.API_URL || 'http://localhost:4010';
async function getUser(id) {
const response = await fetch(`${API_URL}/users/${id}`);
return response.json();
}APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. specification formats
| Format | Best for | Tooling |
|---|---|---|
| OpenAPI 3.x | REST APIs | Excellent, widest ecosystem |
| GraphQL Schema | Flexible queries | Good, schema-first tools |
| gRPC (Protobuf) | High-performance internal APIs | Good, requires more setup |
Key practices
Use consistent naming conventions across all your endpoints. RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. APIs should use nouns (resources), not verbs. HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. methods provide the verbs.
GET /users → list users
POST /users → create user
GET /users/123 → get one user
PUT /users/123 → update user
DELETE /users/123 → delete userVersion your APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. from the start. When you need to make breaking changes, you can do so safely with /v2/users while existing clients continue using /v1/users.
Quick reference
| Benefit | Who it helps most |
|---|---|
| Parallel development | All teams |
| Clear data contracts | Frontend + backend |
| Auto-generated docs | Everyone |
| Mock servers from spec | Frontend, mobile |
| Contract testing | QA, CI pipelines |
| Easy client SDK generation | Third-party integrators |
// API-First Example: OpenAPI Spec
// api-spec.yaml
openapi: 3.0.0
info:
title: Todo API
description: A simple todo list API
version: 1.0.0
paths:
/todos:
get:
summary: List all todos
parameters:
- name: completed
in: query
schema:
type: boolean
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Todo'
example:
- id: 1
title: "Buy groceries"
completed: false
- id: 2
title: "Write docs"
completed: true
post:
summary: Create a todo
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TodoCreate'
example:
title: "New task"
completed: false
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
'400':
description: Invalid input
/todos/{id}:
get:
summary: Get a todo
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Success
'404':
description: Not found
delete:
summary: Delete a todo
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'204':
description: Deleted
'404':
description: Not found
components:
schemas:
Todo:
type: object
required:
- id
- title
- completed
properties:
id:
type: integer
description: Unique identifier
title:
type: string
description: Todo description
completed:
type: boolean
description: Completion status
createdAt:
type: string
format: date-time
TodoCreate:
type: object
required:
- title
properties:
title:
type: string
minLength: 1
maxLength: 200
completed:
type: boolean
default: false
// Now frontend can use mock server:
// npx @stoplight/prism-cli mock api-spec.yaml
// Frontend code (works with mock, then real API)
const API_URL = process.env.API_URL || 'http://localhost:4010';
async function getTodos() {
const response = await fetch(`${API_URL}/todos`);
return response.json();
}
async function createTodo(title) {
const response = await fetch(`${API_URL}/todos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title })
});
return response.json();
}