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`.
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 →