Express Cheatsheet
Express 4 to 5 Migration
Use this Express reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Express 5 at a Glance
Express 5 is the npm latest install (5.0.0 shipped September 2024). It requires Node.js 18+, forwards rejected promises to error middleware automatically, swaps the route matcher to path-to-regexp v8, and removes long-deprecated API signatures.
npm install express # → 5.x npm install express@4 # stay on 4.x
Async Handlers — No More Wrapper
The biggest win: middleware and handlers that return a rejected promise now call next(err) for you.
// Express 5 — a throw inside async lands in the error handler app.get('/users/:id', async (req, res) => { const user = await db.users.findById(req.params.id); // rejection → error middleware if (!user) throw Object.assign(new Error('Not found'), { status: 404 }); res.json(user); });
// Express 4 required try/catch or a wrapper on EVERY async handler const asyncHandler = (fn) => (req, res, next) => fn(req, res, next).catch(next); app.get('/users/:id', asyncHandler(async (req, res) => { /* ... */ }));
Delete express-async-errors / express-async-handler when migrating.
Route Path Syntax Changes (path-to-regexp v8)
These throw TypeError: Missing parameter name (or similar) at startup on Express 5:
| Express 4 | Express 5 |
|---|---|
app.get('*') / app.all('*') | app.get('/*splat') — wildcards must be named; '/{*splat}' also matches / |
'/users/:id?' | '/users{/:id}' — optional segments use braces |
'/files/*' | '/files/*rest' (req.params.rest is an array of segments) |
'/report/:id(\d+)' | Regexp-in-string removed — use a real RegExp or validate in the handler |
'/ab+cd', '/ab?cd', '/ab(cd)?e' | String pattern operators removed — use a RegExp |
Parameter names must now be valid JS identifiers (or quoted: :"user-id").
Removed / Renamed APIs
| Express 4 | Express 5 |
|---|---|
app.del(path, fn) | app.delete(path, fn) |
req.param('name') | req.params.name / req.query.name / req.body.name |
res.send(404) | res.sendStatus(404) |
res.send(body, status) / res.json(obj, status) | res.status(status).send(body) / .json(obj) |
res.redirect(url, status) | res.redirect(status, url) |
res.redirect('back') | res.redirect(req.get('Referrer') || '/') |
res.sendfile() | res.sendFile() |
req.acceptsCharset / Encoding / Language | Pluralized: req.acceptsCharsets() etc. |
Behavior Changes
| What | Express 4 | Express 5 |
|---|---|---|
req.query | Writable object; parsed with qs ('extended') | Read-only getter; default parser 'simple' — ?a[b]=1 no longer nests |
req.body | {} from parsers even on empty/unmatched bodies | undefined when no body was parsed |
express.urlencoded() | extended: true default | extended: false default — pass it explicitly |
res.status(code) | Coerced loosely | Throws unless an integer 100–999 |
req.host | Strips the port | Keeps the port |
res.clearCookie(name, opts) | Accepted maxAge/expires | Ignores maxAge/expires |
| Response compression | gzip/deflate | Adds Brotli (Accept-Encoding: br) support from 5.1 |
Ecosystem Gotchas
express-mongo-sanitizecrashes on v5 (it assigns to the read-onlyreq.query).- Middleware that mutates
req.query(some old sanitizers/HPP setups) needs a v5-compatible release or areq.body-only strategy. @types/expressv5 typings ship under the same package — update it together withexpress.
Migration Checklist
npm install express@latest
npx @expressjs/codemod upgrade # official codemod rewrites most removed signatures- Fix route strings:
'*'→'/*splat',:param?→{/:param}, regex-in-string →RegExp. - Remove async error wrappers; rely on automatic promise rejection forwarding.
- Grep for removed signatures:
res.send(number),res.json(obj, status),req.param(,redirect('back'). - Set
extendedexplicitly onexpress.urlencoded(); re-check any code that writes toreq.query. - Run the app — v5 fails fast at startup on invalid paths, so smoke-starting catches most breakage.