Query Strings

`?page=1&limit=20` — Parameters on `req.query`

Query Strings

Query parameters live on req.query. Always strings; coerce or validate before use.

3 min read Level 1/5 #express#query#request
What you'll learn
  • Read query params
  • Coerce types safely
  • Handle arrays and nested params

Query strings are the ?key=value parts of a URL. Express parses them into req.query.

Basic

app.get("/search", (req, res) => {
  const q = req.query.q;
  const limit = req.query.limit ?? "10";
  res.json({ q, limit });
});

// GET /search?q=express&limit=20
// → { "q": "express", "limit": "20" }

Values are always strings, even numeric-looking ones. Coerce yourself:

const limit = Number(req.query.limit ?? 10);

Arrays

Repeating a key gives an array:

// GET /posts?tag=js&tag=react
// req.query.tag → ['js', 'react']

A single value stays a string. Normalize to always-array:

const tags = [].concat(req.query.tag ?? []);

Nested / Complex

By default Express uses qs for parsing, which supports nested:

// GET /search?filter[city]=NYC&filter[age]=30
// req.query.filter → { city: 'NYC', age: '30' }

Disable nested parsing if you only want simple key/value:

app.set("query parser", "simple");

Validation With Zod

The robust pattern — never trust query types:

import { z } from "zod";

const PaginationSchema = z.object({
  page:  z.coerce.number().int().min(1).default(1),
  limit: z.coerce.number().int().min(1).max(100).default(20),
});

app.get("/posts", (req, res) => {
  const { page, limit } = PaginationSchema.parse(req.query);
  // page, limit are guaranteed numbers within bounds
  res.json({ page, limit });
});

coerce turns "20" into 20, applies min/max, defaults missing values. Three lines that prevent a class of bugs.

URL Length Limits

Browsers and servers cap URL length around 2-8KB. For lots of data, use POST with a body — not a giant query string.

The Response Object →