Map Every HTTP Verb to the Right Handler
HTTP Methods
`@koa/router` exposes a method for every HTTP verb. Pair that with `router.allowedMethods()` to get correct 405 and 501 responses automatically.
What you'll learn
- Register routes for all common HTTP verbs
- Use `router.all` to match any method
- Let `router.allowedMethods()` handle 405 and 501 responses
HTTP methods carry semantic meaning. REST APIs use them to distinguish
reading from writing, replacing from partially updating, and so on.
@koa/router provides a dedicated method for each verb.
All Supported Verbs
router.get("/items", async (ctx) => { /* list */ });
router.post("/items", async (ctx) => { /* create */ });
router.put("/items/:id", async (ctx) => { /* replace*/ });
router.patch("/items/:id", async (ctx) => { /* update */ });
router.delete("/items/:id", async (ctx) => { /* remove */ });
router.head("/items", async (ctx) => { /* meta */ });
router.options("/items", async (ctx) => { /* CORS */ }); router.head is rare because router.allowedMethods() handles HEAD
automatically by stripping the body from a GET response.
Matching Any Method
router.all registers a handler for every HTTP method on a path.
It is handy for logging or per-route rate limiting:
router.all("/items/:id", async (ctx, next) => {
console.log(`${ctx.method} /items/${ctx.params.id}`);
await next(); // continue to the method-specific handler
});
router.get("/items/:id", async (ctx) => { ctx.body = "get"; });
router.delete("/items/:id", async (ctx) => { ctx.body = "deleted"; }); router.allowedMethods() — 405 and 501 Automatic
When you mount router.allowedMethods(), the router inspects every
unhandled request. If the path matched a registered route but
the method did not, it returns:
405 Method Not Allowedwith anAllowheader listing valid methods.501 Not Implementedfor exotic methods the server cannot handle.
app.use(router.routes()).use(router.allowedMethods()); Without allowedMethods() a wrong-method request falls through to
whatever comes next, usually returning 404 — confusing to API clients.
HEAD and OPTIONS in Practice
| Method | @koa/router behaviour |
|---|---|
| HEAD | Mirrors GET handler, strips body automatically |
| OPTIONS | allowedMethods() replies with Allow header |
| GET | Normal handler; body included |
If you need custom OPTIONS logic (e.g. CORS pre-flight), register an
explicit router.options handler before mounting allowedMethods().
CRUD Example
import Koa from "koa";
import Router from "@koa/router";
const app = new Koa();
const router = new Router();
const store = new Map([["1", { id: "1", name: "Widget" }]]);
router.get("/products", async (ctx) => {
ctx.body = [...store.values()];
});
router.get("/products/:id", async (ctx) => {
const item = store.get(ctx.params.id);
if (!item) { ctx.status = 404; ctx.body = { error: "not found" }; return; }
ctx.body = item;
});
router.post("/products", async (ctx) => {
const id = String(store.size + 1);
const item = { id, name: "New Product" };
store.set(id, item);
ctx.status = 201;
ctx.body = item;
});
router.delete("/products/:id", async (ctx) => {
store.delete(ctx.params.id);
ctx.status = 204;
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000); Up Next
As your app grows, splitting routes into separate files and composing them keeps things manageable.
Nested Routers →