Working with Query Strings
1. Accessing Query Parameters
| Property | Description |
req.query | Parsed query object |
req.url | Includes raw query string |
new URL(req.url, "http://x").searchParams | Native URLSearchParams |
Example: Basic access
// GET /search?q=express&page=2
app.get("/search", (req, res) => {
const { q, page } = req.query; // strings
res.json({ q, page });
});
2. Reading Single Query Value
| Parser | ?a=1&b=2 | ?items[a]=1&items[b]=2 |
simple (default) | {a:"1",b:"2"} | {"items[a]":"1"} (literal) |
extended (qs) | {a:"1",b:"2"} | {items:{a:"1",b:"2"}} (nested) |
Example: Switch parser
app.set("query parser", "extended"); // enable nested objects
// Disable parsing entirely (req.query = {})
app.set("query parser", false);
3. Handling Array Query Values
Example: Multi-value query
// GET /tags?tag=js&tag=node&tag=express
app.get("/tags", (req, res) => {
const tags = [].concat(req.query.tag || []); // normalize to array
res.json(tags); // ["js","node","express"]
});
// Or with extended parser: ?tags[]=js&tags[]=node
app.get("/tags2", (req, res) => res.json(req.query.tags));
| Format | Result with extended parser |
?a=1&a=2 | ["1","2"] |
?a[]=1&a[]=2 | ["1","2"] |
?a[0]=1&a[1]=2 | ["1","2"] |
4. Setting Default Query Values
Example: Defaults & coercion
app.get("/posts", (req, res) => {
const page = Math.max(1, Number(req.query.page) || 1);
const limit = Math.min(100, Number(req.query.limit) || 10);
const sort = req.query.sort ?? "createdAt";
res.json({ page, limit, sort });
});
5. Validating Query Parameters
Example: zod schema
import { z } from "zod";
const querySchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
sort: z.enum(["asc", "desc"]).default("desc")
});
app.get("/users", (req, res) => {
const result = querySchema.safeParse(req.query);
if (!result.success) return res.status(400).json(result.error.flatten());
res.json(getUsers(result.data));
});
| Library | Note |
| zod | TS-first, supports coerce |
| express-validator | query() chain |
| joi | Verbose schema DSL |
6. Converting Query Types
| Source | Converter |
"42" | Number(s) or parseInt(s, 10) |
"true" | s === "true" |
"a,b,c" | s.split(",") |
"2024-01-01" | new Date(s) |
| JSON | JSON.parse(s) (catch errors) |
7. Handling Missing Query Parameters
Example: Required vs optional
app.get("/search", (req, res) => {
if (!req.query.q) return res.status(400).json({ error: "q is required" });
const limit = req.query.limit ?? 10; // optional with default
res.json(search(req.query.q, limit));
});
8. Building Query Strings
Example: URLSearchParams
const params = new URLSearchParams({
page: 2,
limit: 20,
sort: "createdAt"
});
const url = `/api/users?${params.toString()}`;
// /api/users?page=2&limit=20&sort=createdAt
// With arrays
const tags = new URLSearchParams();
["js", "node"].forEach(t => tags.append("tag", t));
| Method | Use |
set(k, v) | Replace value |
append(k, v) | Add additional value |
delete(k) | Remove |
toString() | Encoded query string (no leading ?) |
9. Parsing Complex Queries
Example: qs library
import qs from "qs";
app.set("query parser", (str) => qs.parse(str, {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000
}));
// ?filter[status]=active&filter[role][]=admin&filter[role][]=editor
// → req.query.filter = { status: "active", role: ["admin", "editor"] }
Warning: Set arrayLimit, depth, and parameterLimit on qs to prevent prototype pollution & DoS via deeply-nested queries.
10. Implementing Search Queries
Example: Multi-criteria search
app.get("/products", async (req, res) => {
const { search, minPrice, maxPrice, category, sort = "name", page = 1, limit = 20 } = req.query;
const filter = {};
if (search) filter.name = { $regex: search, $options: "i" };
if (category) filter.category = category;
if (minPrice || maxPrice) {
filter.price = {};
if (minPrice) filter.price.$gte = +minPrice;
if (maxPrice) filter.price.$lte = +maxPrice;
}
const docs = await Product.find(filter).sort(sort).skip((page-1)*limit).limit(+limit);
res.json({ data: docs, page: +page, limit: +limit });
});