Predictable APIs
A REST API is predictable: URLs name things (/notes, /notes/7) and HTTP methods say what to do with them. Anyone who knows the convention can guess how to use your API.
The CRUD table
- GET /notes → list (200) · GET /notes/:id → one (200 or 404)
- POST /notes → create (201 + the new note) · invalid input → 400
- PATCH /notes/:id → change some fields (200) · PUT replaces the whole thing
- DELETE /notes/:id → remove (204, no body)
import express from "express";
const app = express();
app.use(express.json());
const notes = new Map();
let nextId = 1;
app.post("/notes", (req, res) => {
const { title } = req.body;
if (typeof title !== "string" || !title.trim()) return res.status(400).json({ error: "title is required" });
const note = { id: nextId++, title: title.trim(), done: false };
notes.set(note.id, note);
res.status(201).json(note);
});
app.get("/notes", (req, res) => res.json([...notes.values()]));
app.patch("/notes/:id", (req, res) => {
const note = notes.get(Number(req.params.id));
if (!note) return res.status(404).json({ error: "Note not found" });
if ("done" in req.body) note.done = Boolean(req.body.done);
res.json(note);
});
app.listen(3000);
const json = (method, path, body) => fetch("http://localhost:3000" + path, { method, headers: { "Content-Type": "application/json" }, body: body && JSON.stringify(body) }).then(async (r) => [r.status, await r.json()]);
console.log(await json("POST", "/notes", { title: "Buy flour" }));
console.log(await json("PATCH", "/notes/1", { done: true }));
console.log(await json("GET", "/notes"));