Express Cheatsheet

Route and Query Parameters

Use this Express reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Route Parameters — req.params

Named URL segments prefixed with :. Values are always strings.

// Route: /users/:id
// URL:   /users/42
app.get('/users/:id', (req, res) => {
  req.params.id; // '42'
});

// Multiple params
// Route: /orgs/:orgId/repos/:repoId
app.get('/orgs/:orgId/repos/:repoId', (req, res) => {
  const { orgId, repoId } = req.params; // { orgId: 'acme', repoId: '7' }
  res.json({ orgId, repoId });
});

Optional Parameters

Append ? to make a segment optional (Express 4 via path-to-regexp).

// Matches /posts and /posts/123
app.get('/posts/:id?', (req, res) => {
  if (req.params.id) {
    res.json({ id: req.params.id }); // specific post
  } else {
    res.json({ all: true });          // all posts
  }
});

Wildcard / Splat

Capture everything after a prefix.

// Express 4: unnamed *
app.get('/files/*', (req, res) => {
  req.params[0]; // 'a/b/c.txt' for /files/a/b/c.txt
});

// Express 5: named wildcard
app.get('/files/*filepath', (req, res) => {
  req.params.filepath; // 'a/b/c.txt'
});

Type Coercion (Manual)

Express never coerces params — always convert explicitly.

app.get('/items/:id', (req, res) => {
  const id = parseInt(req.params.id, 10);
  if (Number.isNaN(id)) return res.status(400).json({ error: 'Invalid id' });
  // use id as number
});

Regex Constraints (Express 4)

// :id must be digits only
app.get('/users/:id(\\d+)', (req, res) => {
  // only matches /users/123, not /users/abc
});

app.param() — Param Pre-processing

Runs once per route with a matching param name, before other handlers.

app.param('userId', async (req, res, next, value) => {
  try {
    const user = await db.users.findById(value);
    if (!user) return res.status(404).json({ error: 'User not found' });
    req.loadedUser = user;
    next();
  } catch (err) { next(err); }
});

// Every route with :userId gets req.loadedUser automatically
app.get('/users/:userId',         (req, res) => res.json(req.loadedUser));
app.put('/users/:userId',         (req, res) => { /* req.loadedUser ready */ });
app.get('/users/:userId/profile', (req, res) => res.json(req.loadedUser.profile));

Multiple params:

app.param(['postId', 'commentId'], async (req, res, next, value, name) => {
  // name = 'postId' or 'commentId', value = the actual string
  req[name] = await db[name + 's'].findById(value);
  if (!req[name]) return res.sendStatus(404);
  next();
});

Query Parameters — req.query

Parsed from the ?key=value portion of the URL. Always strings (or arrays/objects with extended).

// GET /search?q=node&sort=asc&page=2&limit=10
req.query.q;       // 'node'
req.query.sort;    // 'asc'
req.query.page;    // '2'  ← string
req.query.limit;   // '10' ← string
req.query.missing; // undefined

Arrays

// GET /items?tags=js&tags=ts
req.query.tags; // ['js', 'ts']

// GET /items?tags[]=js&tags[]=ts  (extended parser only)
req.query.tags; // ['js', 'ts']

Nested Objects (qs / extended parser)

// GET /items?filter[price][gte]=10&filter[price][lte]=100
req.query.filter; // { price: { gte: '10', lte: '100' } }

Query Parser Settings

// Default in Express 5: 'simple' (Node's querystring, no nesting)
app.set('query parser', 'simple');

// Extended (qs library, supports nested objects) — the Express 4 default
app.set('query parser', 'extended');

// Disable parsing entirely (req.query always {})
app.set('query parser', false);

// Custom parser function
app.set('query parser', (str) => new URLSearchParams(str));

Safe Querying Pattern

app.get('/users', (req, res) => {
  const page  = Math.max(1, parseInt(req.query.page, 10)  || 1);
  const limit = Math.min(100, parseInt(req.query.limit, 10) || 20);
  const sort  = ['name', 'created_at'].includes(req.query.sort)
    ? req.query.sort : 'created_at';
  const order = req.query.order === 'asc' ? 'asc' : 'desc';
  // Never pass req.query directly to SQL — always validate/whitelist
  res.json(db.users.list({ page, limit, sort, order }));
});

Combining Params and Query

// GET /teams/7/players?position=forward&active=true
app.get('/teams/:teamId/players', (req, res) => {
  const teamId   = req.params.teamId;         // '7'
  const position = req.query.position;        // 'forward'
  const active   = req.query.active === 'true'; // boolean coercion
  res.json(db.players.list({ teamId, position, active }));
});

Hash Fragments

Hash (#fragment) is never sent to the server — it lives in the browser only. You cannot read it from req.

Common Validation Patterns

// Validate UUID param
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

app.param('id', (req, res, next, val) => {
  if (!UUID_RE.test(val)) return res.status(400).json({ error: 'Invalid ID format' });
  next();
});

// Validate query with express-validator
const { query, validationResult } = require('express-validator');

app.get('/search',
  query('q').trim().isLength({ min: 1, max: 200 }),
  query('page').optional().isInt({ min: 1 }).toInt(),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
    // ...
  }
);