Query Strings

Read URL Query Parameters From ctx.query

Query Strings

Koa parses the query string automatically and exposes key-value pairs on `ctx.query`, with the raw string on `ctx.querystring`.

2 min read Level 1/5 #koa#routing#query
What you'll learn
  • Read individual query parameters with `ctx.query`
  • Handle array-style parameters
  • Apply defaults when a parameter is absent

The query string is the part of a URL after ?. Koa parses it automatically using Node’s built-in querystring module and makes the result available on ctx.query — no extra package needed.

Reading Parameters

// GET /search?q=koa&limit=10
router.get("/search", async (ctx) => {
  const { q, limit } = ctx.query;
  ctx.body = { query: q, limit };
});
// → { query: "koa", limit: "10" }

All values are strings. Coerce to numbers or booleans when needed.

Applying Defaults

Missing parameters come back as undefined. Use nullish coalescing or destructuring defaults:

router.get("/items", async (ctx) => {
  const page = Number(ctx.query.page ?? "1");
  const size = Number(ctx.query.size ?? "20");

  ctx.body = { page, size };
});

Array-Style Parameters

Repeat a key to send multiple values: /tags?tag=js&tag=node. Koa returns an array when a key appears more than once:

// GET /tags?tag=js&tag=node
router.get("/tags", async (ctx) => {
  const tags = [ctx.query.tag].flat(); // normalize to array
  ctx.body = { tags };
});
// → { tags: ["js", "node"] }

The .flat() trick handles both the single-value string case and the multi-value array case uniformly.

The Raw Query String

When you need the unparsed string (e.g. to forward it to another service), use ctx.querystring:

router.get("/proxy", async (ctx) => {
  const upstream = `https://api.example.com/data?${ctx.querystring}`;
  ctx.body = { upstream };
});

Full Example

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

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

const allUsers = [
  { id: 1, name: "Alice", role: "admin" },
  { id: 2, name: "Bob",   role: "user"  },
  { id: 3, name: "Carol", role: "user"  },
];

router.get("/users", async (ctx) => {
  const { role, q } = ctx.query;

  let results = allUsers;
  if (role) results = results.filter((u) => u.role === role);
  if (q)    results = results.filter((u) => u.name.includes(q));

  ctx.body = results;
});

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

GET /users?role=user&q=Bob filters the list to just Bob.

Up Next

Beyond GET, APIs use POST, PUT, PATCH, and DELETE. The next lesson covers every HTTP verb and when to use each.

HTTP Methods →