CodeVetter · Repo Unpack · Extension points
expressjs/express
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`.
- `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.
- 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.
- `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.
- 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){...}`.
- 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`.
- `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.
- `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.
- `express.Router` and `express.Route` are re-exported from the `router` package, so custom routers can be constructed independently and mounted with `app.use`.
- `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.
- `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.
- 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.
- 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.
- `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.