Resources and methods
A URL names a thing and a method says what to do to it. Build the full set of cat routes, and see why PUT and DELETE can be repeated safely while POST cannot.
What you'll learn
- Design URLs as resources, a collection and its items, with no verbs in the path
- Choose between POST, PUT, PATCH and DELETE by what each one means
- Explain which methods are safe and which are idempotent, and why a client may retry the idempotent ones
An API that grows one route at a time ends up with paths like /getCats, /createCat, /deleteCatById and /updateCatAge, each with its own rules, and every client has to learn them one by one. HTTP already has a better answer, and it is the idea at the heart of what people call REST: a URL names a thing, and the method says what to do to it. Four paths collapse into one, /cats/3, and a client that knows HTTP already knows what GET, PUT and DELETE on it will do.
Resources are nouns
A resource is anything worth naming: a cat, the list of all cats, an owner's cats. Two shapes cover most of an API:
| URL | What it names |
|---|---|
/cats | the collection, every cat |
/cats/3 | one item of it |
/owners/1/cats | a collection that belongs to something |
Paths are plural nouns, and verbs stay out of them. /cats/3/delete is a path pretending to be a method.
Methods are verbs
| Method | On a collection | On an item | Safe | Idempotent |
|---|---|---|---|---|
GET | list it | read it | yes | yes |
POST | add to it; the server picks the id | rarely used | no | no |
PUT | rarely used | store this whole thing here | no | yes |
PATCH | rarely used | change these fields | no | not guaranteed |
DELETE | rarely used | remove it | no | yes |
Safe means the request changes nothing on the server: a crawler, a prefetch or a curious user can send it freely. Idempotent means sending it twice leaves the server in the same state as sending it once. DELETE /cats/3 twice: the cat is gone either way. The second answer is probably a 404 rather than a 204, and that is fine, because idempotence is about the server's state, not about identical responses.
POST is neither. Send POST /cats twice and there are two new cats. This is why it matters: networks fail in the middle, and a client that never heard back cannot tell whether its request arrived. An idempotent request can simply be sent again. A POST cannot, which is why payment APIs make clients send an idempotency key with every create.
PUT replaces, PATCH changes
The two are easy to confuse, and they mean different things. Take a note stored as { "id": 1, "text": "Feed Tom", "pinned": true }:
PUT /notes/1 {"text": "Feed Luna"}
→ {"id": 1, "text": "Feed Luna"} the body is the whole note now; "pinned" is gone
PATCH /notes/1 {"text": "Feed Luna"}
→ {"id": 1, "text": "Feed Luna", "pinned": true} only "text" changed
PUT also names its own URL, so it can create: PUT /notes/9 on a note that does not exist stores it at id 9 and answers 201 Created. That is what makes it idempotent: the second identical PUT finds the note it created and replaces it with the same thing. POST /notes cannot promise that, because the server chooses a new id each time.
Answers that fit
Lesson 3 is about status codes in full. This lesson needs four:
200 OKwith the resource, for a successful read, replace or change;201 Createdfor a new resource, with aLocationheader naming its URL;204 No Contentfor a delete: done, and nothing to say. In Hono,c.body(null, 204);404 Not Foundwhen the item does not exist.
Your task
The app lists the cats and reads one. Add the rest, on the same cats array:
POST /catscreates a cat from the JSON body under the next free id, one more than the highest id in use, and answers201with the cat andLocation: /cats/<id>.PUT /cats/:idstores the body as the whole cat at that id: it replaces an existing cat (200, and any field the body leaves out is gone) or creates one there (201with aLocation).PATCH /cats/:idchanges only the fields the body carries and answers the cat, or404with{ "error": "Cat <id> not found" }.DELETE /cats/:idremoves the cat and answers204with no body, or the same404.
When it fails
- The "next free id" check fails with a duplicate id: the id was
cats.length + 1. After a delete, or after aPUTchose id 7, the length no longer tracks the ids. Use the highest id in use plus one. - "Replace Tom" still shows
grey:PUTmerged into the old cat instead of replacing it. Build a new object from the id and the body. - "Patch Luna" lost her colour:
PATCHreplaced the cat. Merge the body into the stored one. - The first delete answers
200withnullor{}: a 204 has no body at all,c.body(null, 204).
Remember
- URLs are nouns, a collection and its items; the method is the verb.
GETis safe;GET,PUTandDELETEare idempotent and can be retried;POSTis neither.PUTsends the whole resource and may create it at that URL;PATCHsends only changes.- A new resource is
201with aLocation; a delete is204with no body.
Stuck? Show a hint
The next free id is Math.max(0, ...cats.map((x) => x.id)) + 1, which survives deletions and ids chosen by PUT. PUT builds a new object from the id and the body ({ id, ...body }) and puts it in the array's place, or pushes it; PATCH merges into the stored cat with Object.assign. c.body(null, 204) answers with no body.