HTTP Methods

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.

3 min read Level 2/5 #koa#routing#http
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 Allowed with an Allow header listing valid methods.
  • 501 Not Implemented for 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
HEADMirrors GET handler, strips body automatically
OPTIONSallowedMethods() replies with Allow header
GETNormal 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 →