Using "@koa/router"

Register Routes and Mount the Router on Your App

Using "@koa/router"

Create a `Router` instance, register GET and POST handlers, then mount the router on your Koa app with `router.routes()` and `router.allowedMethods()`.

3 min read Level 1/5 #koa#routing#@koa/router
What you'll learn
  • Create a Router instance and register routes
  • Mount the router correctly on a Koa app
  • Build a working GET/POST users example

With @koa/router installed (npm install @koa/router), you can register routes declaratively and mount the whole router as Koa middleware in two lines.

Creating a Router

import Router from "@koa/router";

const router = new Router();

Each router instance keeps its own route table. You can create as many routers as you need and compose them later.

Registering Routes

The router exposes a method for every HTTP verb:

router.get("/users", async (ctx) => {
  ctx.body = { users: [] };
});

router.post("/users", async (ctx) => {
  // ctx.request.body available after body-parser middleware
  ctx.status = 201;
  ctx.body = { created: true };
});

router.delete("/users/:id", async (ctx) => {
  ctx.body = { deleted: ctx.params.id };
});

Handlers are async (ctx) => {} functions. Set ctx.body to send a response; set ctx.status when you need a non-200 code.

Mounting the Router

This step is where many beginners trip up. You must call both router.routes() and router.allowedMethods():

app.use(router.routes()).use(router.allowedMethods());
  • router.routes() — returns a middleware that matches the incoming request and calls the right handler.
  • router.allowedMethods() — returns a middleware that sends a proper 405 Method Not Allowed (or 501 Not Implemented) when a path exists but the HTTP method is not registered.

Full Working Example

import Koa from "koa";
import Router from "@koa/router";

const app = new Koa();
const router = new Router();

const users = [
  { id: "1", name: "Alice" },
  { id: "2", name: "Bob" },
];

router.get("/users", async (ctx) => {
  ctx.body = users;
});

router.post("/users", async (ctx) => {
  const newUser = { id: String(users.length + 1), name: "New User" };
  users.push(newUser);
  ctx.status = 201;
  ctx.body = newUser;
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => console.log("Listening on :3000"));

Run this and GET /users returns the array while POST /users appends to it.

Route Handler Signature

ParameterTypePurpose
ctxKoa.ContextMerged request + response API
next() => PromiseCall next matching middleware

Most handlers do not need next. Pass it when you want to chain handlers or run shared middleware before a specific route.

Up Next

Paths often carry dynamic segments like /users/42. The next lesson covers route parameters.

Route Parameters →