expressjs/express
Lore
Express has been around for almost 17 years. This page traces the eras it has lived through, drawn from History.md and the git log (first commit 2009-06-26, latest commit 2026-04-23, 6,139 commits, 385 unique author emails).
Eras
The TJ years (Jun 2009 – early 2014)
Express was created by TJ Holowaychuk. The first commit lands on 2009-06-26. Through 2010–2013 TJ authored the bulk of the original code — over 3,800 commits in the all-time history are still attributed to tj@vision-media.ca. During this era Express grew from a Sinatra-inspired toy into the dominant Node.js web framework, hitting 3.x and the early 4.x line.
Key events:
- Jun 2009 — Initial commits, project bootstrap.
- Mar 2014 — Express 4.0.0 released, removing the bundled Connect middleware and turning Express into a thin layer on top of standalone packages. This was the first major decoupling of the codebase.
The Doug Wilson stewardship (2014 – ~2020)
Douglas Christopher Wilson (doug@somethingdoug.com) takes over day-to-day maintenance after TJ steps back. He becomes the second most prolific author in the history (~1,230 commits). The 4.x line gets a long succession of patch releases focused on security, dependency hygiene, and incremental polish. Express becomes the standard reference for "stable, boring, well-supported" in the Node ecosystem.
Key events:
- 2014–2018 — Steady 4.x releases (4.10–4.16). Many internals split out into the
expressjs/andpillarjs/orgs:body-parser,serve-static,send,finalhandler,router. - Late 2010s —
path-to-regexp-based routing is moved to its ownrouterpackage; subsequent versions of Express simply re-export it.
The OpenJS / TC era and the long road to v5 (2020 – Sep 2024)
Express joins the OpenJS Foundation. A formal Technical Committee (TC) is established, listed in Readme.md (Ulises Gascón, Jon Church, Wes Todd, Linus Unnebäck, Blake Embrey, Jean Burellier, Rand McKinney, Chris de Almeida). Triagers are added. The 5.0 line, originally branched in 2014 as 5.0.0-alpha.1, finally crosses the finish line.
Key events:
- Feb 2022 —
5.0.0-beta.1based on 4.17.2. Setsquery parserdefault to'simple', requires Node.js 4+, switches tomime-types, and pulls in the newpath-to-regexpparameter modifiers (?,*,+). - Mar 2024 —
5.0.0-beta.2and5.0.0-beta.3incorporate accumulated 4.x changes. - 2024-09-10 — Express 5.0.0 released. Major changes include:
res.status()becomes strict: integers only, range 100–999, throws otherwise.res.redirect('back')andres.location('back')magic strings dropped.res.clearCookieignores user-providedmaxAge/expires.- Many dependency upgrades:
cookie-signature@^1.2.1,merge-descriptors@^2,serve-static@^2.1,accepts@^2,mime-types@^3,type-is@^2,body-parser@^2,send@^1.1.
The 5.x stabilisation (Sep 2024 – present)
After 5.0.0, the cadence becomes "small, frequent, focused":
- 2024-10-08 —
5.0.1: cookie semver lock for CVE-2024-47764. - 2025-03-31 —
5.1.0:Uint8Arrayinres.send(),ETagoption inres.sendFile(), multiple links with samerelinres.links(). Cleanup pass removessetprototypeof,safe-buffer,utils-merge,methods, anddepdfrom the dependency tree. - 2025-12-01 —
5.2.0ships a security fix for CVE-2024-51999, then5.2.1reverts it on the same day after the CVE was rejected and the fix proved to be an unintended breaking change to the extended query parser. A short, instructive saga preserved at the top ofHistory.md. - Through 2026-04 — Master is in "unreleased changes" mode: HTML structure improvements in
res.redirect()(#5167),app.renderacceptingnulloptions (#6903), and ares.send()perf tweak avoiding duplicate Content-Type processing (#6991).
Longest-standing features
| Feature | First seen | Why it survives |
|---|---|---|
app.set / app.get settings store |
Pre-2.0 era (pre-2010) | The single source of truth for per-app config; mounted apps inherit via prototype chain. |
res.send / res.json / res.render |
Pre-2.0 | The user-facing API contract that has shaped Express's identity since the beginning. |
| Template engine pluggability | Pre-2.0 | app.engine(ext, fn) + View.lookup is the original "swap any template engine" abstraction. |
| Mounted sub-apps | 2.x era | app.use('/admin', subApp) with prototype-inherited settings. Still implemented the same way. |
req.accepts / req.is |
Connect-era heritage | Tiny wrappers over the accepts and type-is packages. |
app.METHOD(path, ...) proxies |
Pre-2.0 | Dynamic generation from node:http's METHODS list — has outlasted multiple router rewrites. |
Deprecated / removed features
| Feature | Removed | Replacement |
|---|---|---|
| Bundled Connect middleware | Express 4.0 (Mar 2014) | Use middleware packages directly |
res.send(status, body) and similar overloads |
4.x deprecation, removed in 5.0 | res.status(code).send(body) |
res.redirect('back') / res.location('back') magic |
5.0 (Sep 2024) | res.redirect(req.get('Referrer') ?? '/') |
res.status accepting non-integer/out-of-range values |
5.0 | Pass an integer in [100, 999] — anything else throws |
path-is-absolute dependency |
5.0 | node:path isAbsolute |
setprototypeof, safe-buffer, utils-merge, methods, depd |
5.1.0 (Mar 2025) | Native equivalents (Object.setPrototypeOf, node:buffer, spread, node:http METHODS) |
Built-in bodyParser() middleware on the app |
4.0 | body-parser package, re-exported as express.json/urlencoded/... |
Built-in static() middleware |
4.0 | serve-static package, re-exported as express.static |
Built-in Router implementation |
Late 4.x | External router package, re-exported as express.Router |
Major rewrites and extractions
- Connect → standalone middlewares (2014, Express 4.0). Body parsing, static serving, sessions, and the rest of Connect's middleware were spun out into individual packages. Express became a small core.
- Routing extraction →
routerpackage (late 4.x). The internal Router moved to thepillarjs/routerrepo. Express now re-exports it. - Body parsing →
body-parserpackage (4.x → 5.x).express.json,express.raw,express.text, andexpress.urlencodedare now thin re-exports ofbody-parser's functions. - Dependency diet for 5.1 (Mar 2025). Removed five legacy polyfill-style dependencies in favour of native Node features.
Growth trajectory
- 2009–2013 — Solo project (TJ era), commit cadence routinely > 100/month.
- 2014–2019 — Doug Wilson era, steady ~30/month. Dependency split into many sub-packages.
- 2020–2024 — Slow phase of governance work and the v5 push. Cadence dips into ~10/month.
- 2024–2026 — Modest revival post-v5. Activity is dominated by dependabot bumps, but real fixes and small features keep landing. Triagers and TC turn over occasionally; a long Triagers-emeriti list in
Readme.mdrecords the historical roster.
385 unique author email addresses appear in the all-time log. The vast majority are one-time contributors (typo fixes, doc tweaks). Effective long-term maintenance has always rested on a handful of TC members.
See also
- By the numbers — the present-day snapshot.
- Fun facts — the trivia version of this story.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.