Query Parameters
Query parameters are the ?key=value pairs after a URL's path — the place for optional, modifying input: filtering, sorting, pagination, search terms. Express parses them automatically into req.query, sparing you the manual URL parsing you did with raw Node. The convenience comes with the same caveats — everything is a string, and the parsed shape can surprise you.
Reading req.query
// GET /search?q=node&page=2&sort=recent
app.get('/search', (req, res) => {
const { q, page, sort } = req.query
console.log(q) // 'node'
console.log(page) // '2' ← a STRING, not a number
console.log(sort) // 'recent'
res.json({ q, page, sort })
})The string trap, again
app.get('/products', (req, res) => {
// Coerce + default + clamp — never trust the raw strings:
const page = Math.max(1, parseInt(req.query.page, 10) || 1)
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit, 10) || 20))
const inStock = req.query.inStock === 'true' // explicit boolean
const sort = ['name', 'price', 'recent'].includes(req.query.sort)
? req.query.sort : 'name' // allow-list the value
res.json(queryProducts({ page, limit, inStock, sort }))
})Arrays and objects in the query
// ?tag=js&tag=node
req.query.tag // ['js', 'node'] ← array for repeated keys
// ?filter[status]=open&filter[role]=admin
req.query.filter // { status: 'open', role: 'admin' } ← nested object
// ?ids[]=1&ids[]=2
req.query.ids // ['1', '2']Defensive normalization
function asArray(value) {
if (value === undefined) return []
return Array.isArray(value) ? value : [value] // tolerate single vs many
}
app.get('/items', (req, res) => {
const tags = asArray(req.query.tag) // always an array
res.json(filterByTags(tags))
})Configuring the query parser
Express lets you choose how queries are parsed. The default 'extended' mode (via qs) supports the nested-object/array behavior above; 'simple' mode (querystring) is flatter:
// Disable rich parsing for a flatter, more predictable req.query:
app.set('query parser', 'simple')
// Or a custom parser function:
app.set('query parser', (str) => new URLSearchParams(str))query vs params — a reminder
|
| |
|---|---|---|
Source | Path segments ( | After |
Required? | Yes (part of the route) | Optional |
Use for | Identifying a resource | Filter / sort / paginate |
Example |
|
|