expressjs/express
Architecture
Express is intentionally small. The express package is a thin coordinator that wires together five concerns: an application object, a request prototype, a response prototype, a view loader, and a router (sourced from the external router package). Body parsing and static file serving are also re-exported from external packages.
High-level component diagram
graph TD
User[User code: app.get, app.use, app.listen] --> Factory[express factory<br/>lib/express.js]
Factory --> App[Application prototype<br/>lib/application.js]
Factory --> ReqProto[Request prototype<br/>lib/request.js]
Factory --> ResProto[Response prototype<br/>lib/response.js]
Factory -->|re-exports| BP[body-parser<br/>json/raw/text/urlencoded]
Factory -->|re-exports| SS[serve-static]
Factory -->|re-exports| RouterPkg[router package<br/>Router/Route]
App -->|delegates routing| RouterPkg
App -->|view lookup/render| View[View<br/>lib/view.js]
App -->|http.createServer| Node[node:http]
ReqProto -->|inherits from| IM[http.IncomingMessage]
ResProto -->|inherits from| SR[http.ServerResponse]
App -->|finalhandler on unhandled| FH[finalhandler]Request lifecycle
sequenceDiagram
participant Client
participant Server as node http.Server
participant App as app (function)
participant Router as Router (external)
participant MW as Middleware/Route handler
participant Final as finalhandler
Client->>Server: HTTP request
Server->>App: app(req, res)
App->>App: setPrototypeOf(req, app.request)<br/>setPrototypeOf(res, app.response)
App->>App: set X-Powered-By, res.locals
App->>Router: router.handle(req, res, done)
Router->>MW: match path/method, run stack
MW-->>Router: next(err?) or res.send/json/etc.
Router-->>App: stack exhausted
App->>Final: finalhandler responds 404 or error
Final-->>Client: responseThe application is itself a function. createApplication() in lib/express.js returns a closure that calls app.handle(req, res, next). Inside app.handle (in lib/application.js), Express:
- Builds a
finalhandlerto deal with unhandled errors and 404s. - Sets the
X-Powered-Byheader (when enabled). - Reassigns the prototype chains of
reqandresso request and response helpers (e.g.req.is,res.send) are available. - Initialises
res.locals. - Hands off to the lazily-created top-level
Router(this.router.handle).
Routing itself lives in the external router package. Express re-exports Router and Route so user code can build modular routers (express.Router()).
Source layout
express/
├── index.js # Re-exports lib/express.js
├── lib/
│ ├── express.js # Factory function, default exports, re-exports body-parser/serve-static
│ ├── application.js # app prototype: init, handle, use, set/get, listen, render, METHODS
│ ├── request.js # Request prototype: get/header, accepts*, is, ip, host, query, fresh, ...
│ ├── response.js # Response prototype: status, send, json, sendFile, redirect, render, cookie, ...
│ ├── view.js # Filesystem view lookup and engine dispatch
│ └── utils.js # ETag, query parser, trust proxy, content-type helpers
├── examples/ # 26 runnable examples (auth, mvc, sessions, vhost, ...)
├── test/ # Mocha suites mirroring lib/ (acceptance + unit + fixtures)
├── .github/workflows/ # ci.yml, codeql.yml, scorecard.yml, legacy.yml
└── History.md # Hand-curated changelog dating back to 2010Subsystem map
| Concern | Lives in |
|---|---|
| Factory + exports | lib/express.js |
| Settings/config | app.set/app.get in lib/application.js, helpers in lib/utils.js |
| Routing & middleware | External router package |
| Request helpers | lib/request.js |
| Response helpers | lib/response.js |
| Views/templating | lib/view.js, plus app.render/res.render in application.js/response.js |
| Body parsing | External body-parser (re-exported as express.json, express.urlencoded, express.text, express.raw) |
| Static files | External serve-static (re-exported as express.static) |
| File sending | External send (used by res.sendFile) |
| Error fallback | External finalhandler |
The "thin core, dependency-driven" split is the most important architectural property of Express. Most of what application code thinks of as Express (path-to-regexp matching, multipart-free body parsing, ETag generation) is implemented elsewhere and re-exported here. See Reference: dependencies for the full list.
Inheritance and prototype tricks
Express leans heavily on prototype rewiring rather than classes:
reqandresprototypes are exposed onapp.requestandapp.response. They start asObject.create(http.IncomingMessage.prototype)andObject.create(http.ServerResponse.prototype)respectively.- On every request,
app.handlecallsObject.setPrototypeOf(req, this.request)so user-defined extensions (added viaapp.request.foo = ...) are visible on the actualreq. - Mounted sub-apps inherit settings, engines, and req/res prototypes from their parent via
Object.setPrototypeOfon the'mount'event (seeapp.defaultConfigurationinlib/application.js). merge-descriptors(mixin) copies theEventEmitterandapplicationprototypes onto the callableappfunction soappis simultaneously a function, anEventEmitter, and an Express application.
This is unusual JavaScript, but it lets app(req, res) be a valid Node HTTP request listener while still carrying methods like app.use() and event-emitter behaviour.
Where to read next
- Systems / Application — the deepest single file, covering settings, mounting, and the request entry point.
- Systems / Request and Response — the user-facing helper APIs.
- Features / Routing and middleware — how the external router integrates.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.