Open-Source Wikis

/

Express

/

Express

/

Architecture

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: response

The 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:

  1. Builds a finalhandler to deal with unhandled errors and 404s.
  2. Sets the X-Powered-By header (when enabled).
  3. Reassigns the prototype chains of req and res so request and response helpers (e.g. req.is, res.send) are available.
  4. Initialises res.locals.
  5. 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 2010

Subsystem 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:

  • req and res prototypes are exposed on app.request and app.response. They start as Object.create(http.IncomingMessage.prototype) and Object.create(http.ServerResponse.prototype) respectively.
  • On every request, app.handle calls Object.setPrototypeOf(req, this.request) so user-defined extensions (added via app.request.foo = ...) are visible on the actual req.
  • Mounted sub-apps inherit settings, engines, and req/res prototypes from their parent via Object.setPrototypeOf on the 'mount' event (see app.defaultConfiguration in lib/application.js).
  • merge-descriptors (mixin) copies the EventEmitter and application prototypes onto the callable app function so app is simultaneously a function, an EventEmitter, 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.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – Express wiki | Factory