Designing and Consuming REST APIs
A REST API exposes resources over HTTP, usually returning JSON.
Resource-oriented URLs
| Action | Request | Success |
|---|---|---|
| List | GET /api/notes?limit=20&cursor=... | 200 |
| Read | GET /api/notes/42 | 200 |
| Create | POST /api/notes | 201 with Location header |
| Replace | PUT /api/notes/42 | 200 |
| Update | PATCH /api/notes/42 | 200 |
| Delete | DELETE /api/notes/42 | 204 |
Use nouns (/notes), plural names and nesting only when it expresses ownership (/users/7/notes).
Request and response shape
{
"id": 42,
"text": "Learn REST",
"done": false,
"createdAt": "2026-10-14T09:30:00Z"
}
Errors should have a consistent body:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "text must not be empty",
"fields": { "text": "required" }
}
}
Status codes you will use
| Code | Use |
|---|---|
| 200 / 201 / 204 | OK / Created / No Content |
| 400 | Invalid request |
| 401 | Not authenticated |
| 403 | Authenticated but not allowed |
| 404 | Not found |
| 409 | Conflict (duplicate, version mismatch) |
| 422 | Valid syntax, failed validation |
| 429 | Too many requests |
| 500 | Server error |
Validation example
const { z } = require("zod");
const NoteInput = z.object({
text: z.string().min(1).max(500),
done: z.boolean().optional(),
});
app.post("/api/notes", (req, res) => {
const result = NoteInput.safeParse(req.body);
if (!result.success) {
return res.status(422).json({ error: { code: "VALIDATION_ERROR", fields: result.error.flatten().fieldErrors } });
}
// ... create the note
});
Consuming an API from the browser
async function api(path, options = {}) {
const res = await fetch(`/api${path}`, {
headers: { "Content-Type": "application/json" },
...options,
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error?.message ?? `HTTP ${res.status}`);
}
return res.status === 204 ? null : res.json();
}
const note = await api("/notes", { method: "POST", body: JSON.stringify({ text: "Hi" }) });
CORS in one paragraph
Browsers block cross-origin requests unless the server sends Access-Control-Allow-Origin for your origin. Allow-list the origins you control; never use * with credentials.
See also API design and rate limiting.
Loading Comments...