Routes and errors
Create resources with POST
Parse a JSON request, create a server-owned identifier, and return 201 with the new resource and Location header.
8 minute lesson
POST to a collection asks the server to create a subordinate resource. The server decides the new resource URL.
Parse JSON only when the content type is correct, create the identifier on the server, and return 201 Created. A Location header lets the client find the canonical new resource.
app.post('/books', async c => {
const input = await c.req.json()
const book = { ...input, id: crypto.randomUUID() }
books.push(book)
c.header('Location', `/books/${book.id}`)
return c.json({ book }, 201)
})
Do not spread unknown input into a stored object in production code. Validate and pick the writable fields first, then assign server-owned fields such as id and createdAt afterward so a client cannot override them. Persist the resource before returning 201; a successful response is a promise that the canonical URL can be read.
Classify failures at the boundary. An unsupported content type can return 415, malformed JSON is a 400, and syntactically valid JSON that violates the book schema can return 422. A retry after an uncertain network failure can create a duplicate, so clients must not assume POST is idempotent. The later idempotency lesson adds a deliberate retry contract.
Create a book, follow the Location header, and confirm a subsequent list includes it.
Lesson completed