Route Parameters

Capture Dynamic Segments From the URL Path

Route Parameters

Named segments like `:id` capture variable parts of the URL and expose them on `ctx.params` inside your handler.

3 min read Level 1/5 #koa#routing#params
What you'll learn
  • Define named route parameters with the `:param` syntax
  • Read captured values from `ctx.params`
  • Work with multiple and optional parameters

A route parameter is a named placeholder in the path pattern. When a request URL matches the pattern, @koa/router extracts the placeholder’s value and puts it in ctx.params.

Basic Named Parameter

Prefix a path segment with : to make it a parameter:

router.get("/users/:id", async (ctx) => {
  const { id } = ctx.params;
  ctx.body = { userId: id };
});

A GET /users/42 request sets ctx.params.id to "42". Values are always strings — coerce to a number when needed.

Multiple Parameters

You can use as many named segments as you like:

router.get("/orgs/:orgId/repos/:repoId", async (ctx) => {
  const { orgId, repoId } = ctx.params;
  ctx.body = { orgId, repoId };
});

GET /orgs/acme/repos/api-server gives { orgId: "acme", repoId: "api-server" }.

Optional Parameters

Append ? to make a segment optional:

router.get("/posts/:year/:month?", async (ctx) => {
  const { year, month } = ctx.params;
  if (month) {
    ctx.body = { year, month };
  } else {
    ctx.body = { year, month: "all" };
  }
});

Both /posts/2024 and /posts/2024/06 will match.

Wildcard Catch-All

Use (.*) to match everything after a prefix:

router.get("/files/(.*)", async (ctx) => {
  ctx.body = { file: ctx.params[0] };
});

/files/assets/logo.svgctx.params[0] is "assets/logo.svg".

Full Example

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

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

const db = {
  "1": { id: "1", name: "Alice" },
  "2": { id: "2", name: "Bob" },
};

router.get("/users/:id", async (ctx) => {
  const user = db[ctx.params.id];
  if (!user) {
    ctx.status = 404;
    ctx.body = { error: "user not found" };
    return;
  }
  ctx.body = user;
});

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

Parameter Constraints

@koa/router uses path-to-regexp under the hood. You can add inline regex constraints:

// Only match numeric IDs
router.get("/users/:id(\\d+)", async (ctx) => {
  ctx.body = { id: Number(ctx.params.id) };
});

Non-numeric IDs will fall through to the next matching route (or produce a 404).

Up Next

Query strings let callers pass optional filters and options without changing the URL structure.

Query Strings →