Express Cheatsheet

Routing

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

Basic Route Definition

app.METHOD(path, ...handlers)
MethodUsage
app.get(path, cb)Handle GET
app.post(path, cb)Handle POST
app.put(path, cb)Handle PUT
app.patch(path, cb)Handle PATCH
app.delete(path, cb)Handle DELETE
app.head(path, cb)Handle HEAD
app.options(path, cb)Handle OPTIONS
app.all(path, cb)Match ANY method
app.get('/users', (req, res) => res.json(users));
app.post('/users', (req, res) => res.status(201).json(created));
app.put('/users/:id', (req, res) => res.json(updated));
app.delete('/users/:id', (req, res) => res.sendStatus(204));

// Match every method on every path (catch-all, use carefully)
app.all('/{*splat}', (req, res) => res.status(405).send('Method not allowed'));
// Express 4 only: app.all('*', ...) — throws a path-to-regexp error on Express 5

Route Paths

Paths can be exact strings, strings with named parameters/wildcards, or regular expressions. Express 5 uses path-to-regexp v8; Express 4 used v0.1.x with a different pattern syntax.

// Exact string
app.get('/about', handler);

// Named parameter
app.get('/users/:id', handler);        // req.params.id

// Optional segment (Express 5 brace syntax)
app.get('/users{/:id}', handler);      // matches /users and /users/42
app.get('/files/:name{.:ext}', handler); // matches /files/a and /files/a.txt

// Named wildcard (Express 5) — matches one or more segments
app.get('/files/*filepath', handler);  // req.params.filepath = ['a', 'b', 'c']

// RegExp (works in both majors)
app.get(/\/users\/\d+/, handler);      // matches /users/123 etc.

Legacy — Express 4 only. These string patterns throw path-to-regexp errors on Express 5:

app.get('/ab?cd', handler);     // v5: use /ab{c}d-style braces instead of ?
app.get('/ab+cd', handler);     // v5: + repetition removed — use a RegExp
app.get('/ab*cd', handler);     // v5: wildcards must be named (*splat) and fill a whole segment
app.get('/ab(cd)?e', handler);  // v5: regex groups inside strings removed — use a RegExp
app.get('/users/:id?', handler); // v5: :id? optional marker → '/users{/:id}'

Route Handlers (Chaining)

Multiple handlers are called in order; pass control with next().

// Two handlers
app.get('/secure', authenticate, (req, res) => {
  res.send(`Hello ${req.user.name}`);
});

// Array of handlers
const steps = [logRequest, checkCache, fetchData];
app.get('/data', steps, sendResponse);

// Chained .get()/.post() on same path
app.route('/books')
  .get((req, res) => res.json(books))
  .post((req, res) => res.status(201).json(created))
  .put((req, res) => res.json(updated));

express.Router

Group routes into modular, mountable mini-apps.

// routes/users.js
const router = require('express').Router();

// Middleware scoped to this router
router.use(authenticate);

router.get('/',     (req, res) => res.json(all));
router.get('/:id',  (req, res) => res.json(one));
router.post('/',    (req, res) => res.status(201).json(created));
router.put('/:id',  (req, res) => res.json(updated));
router.delete('/:id', (req, res) => res.sendStatus(204));

module.exports = router;
// app.js — mount with a prefix
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);
// → GET /users, GET /users/:id, POST /users, …

Router Options

const router = express.Router({
  caseSensitive: true,   // match case of app setting
  strict: false,         // allow optional trailing slash
  mergeParams: true,     // inherit req.params from parent router
});

mergeParams: true is needed when a child router uses params defined in the parent's app.use() mount path: ``js app.use('/users/:userId/posts', postsRouter); // postsRouter can read req.params.userId only if mergeParams: true ``

Nested Routers

// routes/api.js
const router = express.Router();
router.use('/users',    require('./users'));
router.use('/products', require('./products'));
module.exports = router;

// app.js
app.use('/api/v1', require('./routes/api'));
// → /api/v1/users, /api/v1/products

Route Ordering and Precedence

Routes are matched top-to-bottom in registration order. First match wins.

// Specific routes BEFORE generic ones
app.get('/users/me',  getMeHandler);   // ✓ matched first
app.get('/users/:id', getByIdHandler); // would swallow 'me' if registered first

// Static segments beat params
app.get('/files/upload', uploadHandler);  // ✓ wins over /files/:name for /files/upload
app.get('/files/:name',  serveFile);

app.param() — Route Parameter Pre-processing

Runs before any handler that has a matching param name.

// Automatically load user for any route with :userId
app.param('userId', async (req, res, next, id) => {
  try {
    req.targetUser = await User.findById(id);
    if (!req.targetUser) return res.status(404).json({ error: 'User not found' });
    next();
  } catch (err) {
    next(err);
  }
});

app.get('/users/:userId', (req, res) => res.json(req.targetUser));
app.put('/users/:userId', (req, res) => { /* req.targetUser already loaded */ });

Wildcard and Catch-All Routes

// 404 catch-all — must be LAST, after all real routes.
// app.use() with no path matches everything on both Express 4 and 5.
app.use((req, res) => {
  res.status(404).json({ error: `Cannot ${req.method} ${req.path}` });
});

// Method-specific catch-all (Express 5): wildcards must be named
app.get('/*splat', (req, res) => res.status(404).send('Page not found'));   // everything except '/'
app.get('/{*splat}', (req, res) => res.status(404).send('Page not found')); // including '/'

app.get('*') / app.all('*') are Express 4 syntax — on Express 5 they throw TypeError: Missing parameter name at startup. Use a named wildcard (/*splat) or a pathless app.use().

Conditional Route Skipping — next('route')

Skip remaining handlers in the current route and jump to the next matching route.

app.get('/items/:id',
  (req, res, next) => {
    if (req.params.id === 'special') return next('route'); // skip to next .get('/items/:id')
    next(); // continue in this route
  },
  (req, res) => res.send('Normal item')
);

app.get('/items/:id', (req, res) => res.send('Special item'));

next('router')

Skip the rest of the current router entirely.

router.use((req, res, next) => {
  if (!req.headers.authorization) return next('router'); // bail out of this router
  next();
});