# Extension points — expressjs/express

> Canonical page: https://codevetter.com/unpack/express/extension-points

# Extension points — expressjs/express

Scanned commit 9a34acf03cb818ff3f8bc40e44176e277a25cbb9 (2026-09-15). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Express is designed to be extended by mutation rather than by configuration. Three exported prototypes (`express.application`, `express.request`, `express.response`) are public and explicitly documented as mutable by the test suite. Beyond that there are four first-class registries — the template-engine map, the `view` constructor, the compiled-function settings (`etag`/`query parser`/`trust proxy`), and route params — plus mounting, which composes whole apps. The verb table is itself generated from Node's `http.METHODS`, so new HTTP methods appear automatically.
- `app.engine(ext, fn)` is the template-engine registry: it normalises the leading dot and stores into `this.engines[extension]`, which `View` consults before falling back to a dynamic `require`. — [lib/application.js#L280-L295](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L280-L295) [lib/view.js#L74-L88](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/view.js#L74-L88) [test/app.engine.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/app.engine.js)
- `app.set('view', CustomViewCtor)` swaps the entire view-resolution class — `app.render` reads `this.get('view')` and does `new View(name, { defaultEngine, root, engines })`, requiring only a `.path` property and a `.render(options, cb)` method. — [lib/application.js#L136](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L136) [lib/application.js#L548-L568](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L548-L568) [examples/view-constructor/index.js#L28-L30](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/view-constructor/index.js#L28-L30) [examples/view-constructor/github-view.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/view-constructor/github-view.js)
- The three compiler settings all short-circuit on a function argument — `compileETag`, `compileQueryParser` and `compileTrust` each begin with `if (typeof val === 'function') return val`, so any of them can be fully replaced by user code. — [lib/utils.js#L130-L215](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/utils.js#L130-L215) [test/config.js#L45-L62](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/config.js#L45-L62)
- `express.application`, `express.request` and `express.response` are exported prototypes and the test suite asserts that mutating them affects all subsequently created apps — the documented monkey-patch seam. — [lib/express.js#L62-L64](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/express.js#L62-L64) [test/exports.js#L52-L70](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/exports.js#L52-L70)
- The idiomatic per-app variant of that seam is mutating `app.response` / `app.request` rather than the global prototype, as `examples/mvc` does by defining `app.response.message = function(msg){...}`. — [examples/mvc/index.js#L22-L30](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/mvc/index.js#L22-L30) [lib/express.js#L44-L51](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/express.js#L44-L51)
- The HTTP verb table is not hardcoded — `exports.methods` maps `require('node:http').METHODS` to lowercase and `methods.forEach` generates both `app[method]` and the per-method registration used by `app.all`. — [lib/utils.js#L29](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/utils.js#L29) [lib/application.js#L471-L501](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L471-L501)
- `app.param(name, fn)` accepts either a single name or an array (recursing per element) and forwards to `this.router.param`, giving a named hook for pre-resolving route parameters. — [lib/application.js#L308-L322](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L308-L322) [test/app.param.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/app.param.js) [examples/params/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/params/index.js)
- `app.use(path, subApp)` is the composition primitive: any object with `.handle` and `.set` is treated as a mountable app, receives `mountpath`/`parent`, and emits `'mount'` — which applications may listen to for their own inheritance logic. — [lib/application.js#L205-L241](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L205-L241) [examples/multi-router/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/multi-router/index.js)
- `express.Router` and `express.Route` are re-exported from the `router` package, so custom routers can be constructed independently and mounted with `app.use`. — [lib/express.js#L70-L71](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/express.js#L70-L71) [test/Router.js#L7-L30](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/Router.js#L7-L30) [examples/multi-router/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/multi-router/index.js)
- `res.format(obj)` is a per-response dispatch table keyed by MIME type or extension name, with an optional `default` key — the content-negotiation extension point, and itself the mechanism behind `res.redirect`'s body selection. — [lib/response.js#L573-L604](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/response.js#L573-L604) [lib/response.js#L841-L858](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/response.js#L841-L858) [examples/content-negotiation/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/content-negotiation/index.js)
- `app.locals` and `res.locals` are the two data-injection points into rendering; `app.render` merges them in a fixed order with caller options, and `app.locals.settings` is pre-wired so templates can read app settings. — [lib/application.js#L125-L131](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L125-L131) [lib/application.js#L536-L540](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/application.js#L536-L540) [examples/view-locals/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/view-locals/index.js)
- Route organisation patterns are demonstrated as extension idioms rather than framework features: `examples/route-map` builds routes from a nested object literal, and `examples/mvc/lib/boot.js` auto-loads controllers into the app. — [examples/route-map/index.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/route-map/index.js) [examples/mvc/lib/boot.js](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/mvc/lib/boot.js) [examples/mvc/index.js#L74](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/examples/mvc/index.js#L74)
- Middleware re-exports (`express.json`, `express.raw`, `express.text`, `express.urlencoded`, `express.static`) are plain property assignments on the module export, so the whole body-parsing layer can be swapped by replacing the dependency. — [lib/express.js#L77-L81](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/express.js#L77-L81) [test/exports.js#L11-L35](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/test/exports.js#L11-L35)
- `res.type`/`res.contentType` and `normalizeType`/`normalizeTypes` centralise MIME handling through `mime-types`, so extension-name support is inherited rather than enumerated in this repo. — [lib/response.js#L507-L513](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/response.js#L507-L513) [lib/utils.js#L61-L80](https://github.com/expressjs/express/blob/9a34acf03cb818ff3f8bc40e44176e277a25cbb9/lib/utils.js#L61-L80)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
