Beginner · Planning an app · topic 7 of 34 · 25 min
Writing the API contract
Explanation
What is an API contract?
An API (Application Programming Interface) is the set of requests a backend accepts. A contract is the written list of those requests, with the exact shape of what goes in and what comes out.
Every endpoint has five parts
- Method: what kind of action (GET reads, POST creates, PATCH changes part of something, DELETE removes).
- Path: the address, such as /api/notes/:id. ":id" is a placeholder for a real number like 7.
- Request body: the JSON the client sends (only for POST and PATCH here).
- Response: the JSON the server sends back when things go well.
- Status codes: the number that says how it went (200 OK, 201 Created, 400 bad input, 401 not logged in, 404 not found).
| Method and path | Request body | Success | Errors |
|---|---|---|---|
| GET /api/notes | - | 200, { notes: [...] } | 401 |
| POST /api/notes | { title, body, pinned } | 201, the new note | 400, 401 |
| GET /api/notes/:id | - | 200, the note | 401, 404 |
| PATCH /api/notes/:id | any of { title, body, pinned } | 200, the updated note | 400, 401, 404 |
| DELETE /api/notes/:id | - | 204, no body | 401, 404 |
| POST /api/auth/signup | { name, email, password } | 201, { user } | 400, 409 (email taken) |
| POST /api/auth/login | { email, password } | 200, { user } | 400, 401 |
| POST /api/auth/logout | - | 204 | - |
// Request: POST /api/notes
{ "title": "Physics formulas", "body": "V = IR", "pinned": true }
// Response: 201 Created
{
"id": 12,
"title": "Physics formulas",
"body": "V = IR",
"pinned": true,
"created_at": "2026-02-01 08:00",
"updated_at": "2026-02-01 08:00"
}One shape for every error
Decide one error shape and use it everywhere. Our Notes app always answers errors as { "error": { "code": "...", "message": "..." } }. The code is short and fixed, for programs to check ("VALIDATION_ERROR", "NOT_FOUND"). The message is friendly text a person can read.
// Response: 400 Bad Request
{ "error": { "code": "VALIDATION_ERROR", "message": "Title is required" } }Contract first, then build in parallel
Contract-first means you write and agree on the contract before writing the code. The frontend developer can then build screens against a mock server: a fake backend that returns the example responses from the contract. The backend developer builds the real thing at the same time.
Teams often write contracts in OpenAPI, a standard format (YAML or JSON) that describes every endpoint. Tools read an OpenAPI file to make documentation pages, mock servers and even test checks automatically.
Watch the lesson
Writing the API contract · 1/6
The frontend and the backend must agree on how they talk. That written agreement is the API contract.
Prefer a video by a teacher? Find free videos on “api contract first design openapi explained for beginners” on YouTube ↗
In real life: A train booking app and its partner apps
A railway booking system offers an API that travel apps use to search trains and check seat availability. Many companies build apps on it, and none of them can see the backend code. They work only from the contract: the path to search trains, the fields to send (from station, to station, date) and the exact JSON that comes back, including the error when a train is fully booked. If the railway changed a field name without notice, every partner app would break the same morning.
Practice (0/3 done)
Write makeError(code, message) that returns an error body in the Notes app shape: { error: { code, message } }. Example: makeError("NOT_FOUND", "Note not found").
Write matchesContract(response, contract). A response looks like { status, body }. A contract looks like { status: 201, body: { id: "number", title: "string", pinned: "boolean" } }, listing required keys and their typeof type. Return true only if the status is equal AND every key in contract.body exists in response.body with the right type (extra keys are fine). Otherwise return false.
Write buildCreated(input, id, now) that builds the response the contract promises for POST /api/notes. Return { status: 201, body } where body has these keys in this order: id, title (input.title trimmed), body (input.body, or "" if missing), pinned (true only if input.pinned is true), created_at (now), updated_at (now).
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.