# Express Administrator & Developer Guide

## 1. Architecture Overview

Express is a minimal, unopinionated web framework for Node.js that provides a thin abstraction over Node's built-in `http` module. It implements a middleware pipeline architecture where incoming HTTP requests flow through an ordered chain of handler functions, each with the ability to modify the request/response objects, short-circuit the pipeline, or delegate to the next handler via `next()`.

The framework consists of five core modules totaling approximately 62K characters of source code, plus a View class for template rendering. Express delegates routing to the external `router` package (v2.2.0) and body parsing to `body-parser` (v2.2.1), keeping the core focused on application lifecycle, request/response augmentation, and middleware orchestration.

### Component Architecture

```text
                         express()
                            │
                    ┌───────┴───────┐
                    │  Application  │  (lib/application.js)
                    │  EventEmitter │
                    │    mixin      │
                    └───────┬───────┘
                            │
              ┌─────────────┼─────────────┐
              │             │             │
         ┌────┴────┐  ┌────┴────┐  ┌─────┴─────┐
         │ Request │  │Response │  │   Router   │
         │  proto  │  │  proto  │  │  (external)│
         │  (req)  │  │  (res)  │  │            │
         └─────────┘  └─────────┘  └──────┬─────┘
                                          │
                                    ┌─────┴─────┐
                                    │   Route    │
                                    │  (external)│
                                    └───────────┘
              ┌─────────┐
              │  View   │  (lib/view.js)
              └─────────┘

    Built-in Middleware (re-exported from dependencies):
    ├── express.json()        ← body-parser
    ├── express.urlencoded()  ← body-parser
    ├── express.raw()         ← body-parser
    ├── express.text()        ← body-parser
    └── express.static()      ← serve-static
```

### Component Inventory

| Component | Path | Role |
|---|---|---|
| Entry point | `index.js` | Re-exports `lib/express.js` |
| Factory + Exports | `lib/express.js` | `createApplication()` factory; exposes `Router`, `Route`, middleware |
| Application | `lib/application.js` | App prototype: settings, routing delegation, `listen()`, `render()` |
| Request | `lib/request.js` | Extended `IncomingMessage` prototype: `query`, `ip`, `accepts`, `is` |
| Response | `lib/response.js` | Extended `ServerResponse` prototype: `send`, `json`, `redirect`, `render` |
| Utilities | `lib/utils.js` | ETag compilation, query parser compilation, trust proxy compilation, MIME normalization |
| View | `lib/view.js` | Template engine lookup, caching, and rendering |

### Technology Stack

| Layer | Technology |
|---|---|
| Runtime | Node.js >= 18 |
| HTTP server | Node.js `http.createServer()` |
| Routing | `router` v2.2.0 (external package) |
| Body parsing | `body-parser` v2.2.1 |
| Static files | `serve-static` v2.2.0 |
| File streaming | `send` v1.1.0 |
| Cookie handling | `cookie` v0.7.1 + `cookie-signature` v1.2.1 |
| Content negotiation | `accepts` v2.0.0 |
| Query parsing | `qs` v6.14.2 (extended mode) / Node `querystring` (simple mode) |
| Proxy trust | `proxy-addr` v2.0.7 |
| ETag generation | `etag` v1.8.1 |
| Cache freshness | `fresh` v2.0.0 |
| Debug logging | `debug` v4.4.0 |

### Data Flow

```text
Client Request
     │
     ▼
http.createServer(app)
     │
     ▼
app.handle(req, res)
  ├── Set X-Powered-By header (if enabled)
  ├── Set req.res / res.req cross-references
  ├── Set prototypes: req → app.request, res → app.response
  ├── Initialize res.locals
  │
  ▼
Router.handle(req, res, finalhandler)
  ├── Match route by method + path
  ├── Execute middleware stack in order
  ├── Parameter pre-processing (app.param callbacks)
  │
  ▼
Route Handler
  ├── Read req.params, req.query, req.body
  ├── Build response via res.send/json/render/redirect
  │
  ▼
finalhandler (if no route matched or error)
  └── 404 or error response
```

> [!NOTE]
> The Router is lazily instantiated on first use (first `app.use()`, `app.get()`, etc.). This means an Express app with no routes registered never allocates a Router instance.

## 2. System Requirements

| Requirement | Detail |
|---|---|
| Node.js | >= 18 (specified in `package.json` `engines` field) |
| npm | Any version bundled with supported Node.js |
| Disk | Minimal — `express` + dependencies total ~1.5 MB in `node_modules` |
| Memory | Framework overhead is negligible; memory usage depends on application middleware and data |
| OS | All platforms supported by Node.js (Linux, macOS, Windows, etc.) |

### Production Dependencies (27 packages)

| Package | Version | Purpose |
|---|---|---|
| `accepts` | ^2.0.0 | Content negotiation |
| `body-parser` | ^2.2.1 | Request body parsing |
| `content-disposition` | ^1.0.0 | Content-Disposition header |
| `content-type` | ^1.0.5 | Content-Type parsing |
| `cookie` | ^0.7.1 | Cookie serialization |
| `cookie-signature` | ^1.2.1 | Cookie signing |
| `debug` | ^4.4.0 | Debug logging |
| `depd` | ^2.0.0 | Deprecation notices |
| `encodeurl` | ^2.0.0 | URL encoding |
| `escape-html` | ^1.0.3 | HTML escaping |
| `etag` | ^1.8.1 | ETag generation |
| `finalhandler` | ^2.1.0 | Default error handler |
| `fresh` | ^2.0.0 | Freshness checking |
| `http-errors` | ^2.0.0 | HTTP error objects |
| `merge-descriptors` | ^2.0.0 | Property descriptor merging |
| `mime-types` | ^3.0.0 | MIME type lookup |
| `on-finished` | ^2.4.1 | Response finish event |
| `once` | ^1.4.0 | One-time function wrapper |
| `parseurl` | ^1.3.3 | URL parsing with caching |
| `proxy-addr` | ^2.0.7 | Client address determination |
| `qs` | ^6.14.2 | Extended query string parser |
| `range-parser` | ^1.2.1 | Range header parsing |
| `router` | ^2.2.0 | HTTP routing |
| `send` | ^1.1.0 | Static file streaming |
| `serve-static` | ^2.2.0 | Static file middleware |
| `statuses` | ^2.0.1 | HTTP status codes |
| `type-is` | ^2.0.1 | Content-Type checking |
| `vary` | ^1.1.2 | Vary header management |

### Development Dependencies

| Package | Version | Purpose |
|---|---|---|
| `mocha` | ^10.7.3 | Test runner |
| `supertest` | ^6.3.0 | HTTP assertion library |
| `nyc` | ^17.1.0 | Code coverage |
| `eslint` | 8.47.0 | Linting |
| `ejs` | ^3.1.10 | Test template engine |
| `hbs` | 4.2.0 | Handlebars test engine |
| `marked` | ^15.0.3 | Markdown test engine |
| `express-session` | ^1.18.1 | Session example/tests |
| `connect-redis` | ^8.0.1 | Redis session example |
| `cookie-parser` | 1.4.7 | Cookie parsing tests |
| `cookie-session` | 2.1.1 | Cookie session example |
| `method-override` | 3.0.0 | HTTP method override tests |
| `morgan` | 1.10.1 | HTTP logger example |
| `pbkdf2-password` | 1.2.1 | Auth example |
| `vhost` | ~3.0.2 | Virtual host example |
| `after` | 0.8.2 | Test utility |

## 3. Configuration Reference

### Application Settings (Complete)

Settings are managed via `app.set(key, value)` / `app.get(key)`, with convenience methods `app.enable(key)`, `app.disable(key)`, `app.enabled(key)`, `app.disabled(key)`.

| Setting | Type | Default | Description | Compiled? |
|---|---|---|---|---|
| `env` | String | `process.env.NODE_ENV` or `'development'` | Environment mode | No |
| `etag` | String/Boolean/Function | `'weak'` | ETag generation. `'weak'` = weak validator, `'strong'` = strong, `false` = disabled, function = custom | Yes → `etag fn` |
| `jsonp callback name` | String | `'callback'` | Query parameter for JSONP callback name | No |
| `json escape` | Boolean | undefined | Escape `<`, `>`, `&` in `res.json()` / `res.jsonp()` output | No |
| `json replacer` | Function | undefined | Custom replacer for `JSON.stringify()` | No |
| `json spaces` | Number | undefined | Indentation spaces for `JSON.stringify()` | No |
| `query parser` | String/Boolean/Function | `'simple'` | `'simple'` = `querystring.parse`, `'extended'` = `qs.parse` with `allowPrototypes`, `false` = disabled, function = custom | Yes → `query parser fn` |
| `subdomain offset` | Number | `2` | Number of dot-delimited parts of the host to remove to access subdomains | No |
| `trust proxy` | Boolean/String/Number/Array/Function | `false` | Proxy trust configuration for `req.ip`, `req.hostname`, `req.protocol` | Yes → `trust proxy fn` |
| `views` | String/Array | `resolve('views')` | Template file lookup directory/directories | No |
| `view cache` | Boolean | `true` when `env === 'production'` | Cache resolved view paths | No |
| `view engine` | String | undefined | Default template engine extension (e.g., `'ejs'`) | No |
| `view` | Constructor | Built-in `View` class | View constructor for template resolution | No |
| `x-powered-by` | Boolean | `true` | Add `X-Powered-By: Express` response header | No |
| `case sensitive routing` | Boolean | `false` | Case-sensitive route matching | No (passed to Router constructor) |
| `strict routing` | Boolean | `false` | Strict slash handling (`/foo` ≠ `/foo/`) | No (passed to Router constructor) |

> [!IMPORTANT]
> Three settings trigger **compilation** when set: `etag`, `query parser`, and `trust proxy`. Setting these stores both the raw value and a compiled function (e.g., `etag fn`). The compiled function is what the framework actually calls at runtime. Setting these to invalid values throws a `TypeError`.

### Compiled Setting Details

**`etag` compilation** (`compileETag` in `lib/utils.js`):

| Input | Compiled Function |
|---|---|
| `'weak'` or `true` | `exports.wetag` — weak ETag via `etag` package |
| `'strong'` | `exports.etag` — strong ETag via `etag` package |
| `false` | `undefined` (no ETag generation) |
| Function | Used as-is |
| Other | Throws `TypeError: unknown value for etag function: <val>` |

**`query parser` compilation** (`compileQueryParser` in `lib/utils.js`):

| Input | Compiled Function |
|---|---|
| `'simple'` or `true` | `querystring.parse` (Node built-in) |
| `'extended'` | `qs.parse` with `{ allowPrototypes: true }` |
| `false` | `undefined` (query returns empty object) |
| Function | Used as-is |
| Other | Throws `TypeError: unknown value for query parser function: <val>` |

**`trust proxy` compilation** (`compileTrust` in `lib/utils.js`):

| Input | Compiled Function |
|---|---|
| `true` | Always returns `true` (trust all) |
| `false` | `proxyaddr.compile([])` (trust none) |
| Number `n` | Trust first `n` hops: `(addr, i) => i < n` |
| String | Split on commas, pass to `proxyaddr.compile()` |
| Array | Pass directly to `proxyaddr.compile()` |
| Function | Used as-is |

> [!WARNING]
> Setting `trust proxy` to `true` means Express trusts **every** proxy in the chain. In production, use a number (hop count) or specific IP addresses/subnets to limit trust.

### Environment Variables

| Variable | Read By | Effect |
|---|---|---|
| `NODE_ENV` | `app.defaultConfiguration()` | Sets `app.get('env')`. When `'production'`: enables view caching, suppresses error stack traces in `finalhandler` |
| `DEBUG` | `debug` package | Enables debug logging. Use `express:*` for all Express debug output |
| `NO_DEPRECATION` | `depd` package | Suppress deprecation warnings |

### Setting Inheritance

When a sub-app is mounted via `app.use(path, subApp)`, the child's `mount` event fires and the child inherits from the parent:

- `this.settings` prototype → `parent.settings` (settings chain via `Object.setPrototypeOf`)
- `this.engines` prototype → `parent.engines`
- `this.request` prototype → `parent.request`
- `this.response` prototype → `parent.response`
- `trust proxy` is inherited if the child still has the default value

## 4. Deployment & Scaling

### Basic Production Deployment

```javascript
const express = require('express');
const app = express();

// Production settings
app.set('env', 'production');
app.set('trust proxy', 1);
app.disable('x-powered-by');

app.listen(process.env.PORT || 3000);
```

### Running Behind a Reverse Proxy

Express is typically deployed behind nginx, HAProxy, or a cloud load balancer. Configure `trust proxy` to correctly resolve client IP and protocol:

```javascript
// Trust one proxy hop (most common)
app.set('trust proxy', 1);

// Trust specific proxy IPs
app.set('trust proxy', 'loopback, 10.0.0.0/8');

// Trust a subnet
app.set('trust proxy', ['10.0.0.0/8', '172.16.0.0/12']);
```

> [!TIP]
> Use `app.set('trust proxy', 1)` for single-proxy setups (the most common production configuration). This trusts only the first hop, using `X-Forwarded-For`, `X-Forwarded-Proto`, and `X-Forwarded-Host` headers from that proxy.

### Clustering

Express itself is single-threaded. Use Node's `cluster` module or a process manager to scale across CPU cores:

```javascript
const cluster = require('node:cluster');
const os = require('node:os');

if (cluster.isPrimary) {
  const cpus = os.cpus().length;
  for (let i = 0; i < cpus; i++) {
    cluster.fork();
  }
} else {
  const app = require('./app');
  app.listen(3000);
}
```

### Docker Deployment

```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
USER node
CMD ["node", "app.js"]
```

> [!NOTE]
> Express has no native Docker integration. The framework runs as a standard Node.js process inside any container.

### Process Managers

| Manager | Use Case |
|---|---|
| PM2 | Production process management with clustering, monitoring, log management |
| systemd | Linux service management |
| Docker + orchestrator | Container-based deployment (Kubernetes, ECS, etc.) |

## 5. Operations

### Health Checks

Express does not include a built-in health endpoint. Implement one:

```javascript
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok', uptime: process.uptime() });
});
```

### Logging

Express has no built-in request logger. Use the `morgan` middleware (included as a dev dependency):

```javascript
const morgan = require('morgan');
app.use(morgan('combined'));  // Apache combined log format
```

> [!TIP]
> In production, pipe morgan output to a log aggregation system rather than stdout. Use `morgan('combined', { stream: accessLogStream })` with a write stream.

### Debug Logging

Express uses the `debug` package with the `express:` namespace:

```bash
# All Express debug output
DEBUG=express:* node app.js

# Application-level only
DEBUG=express:application node app.js

# Router-level only
DEBUG=express:router node app.js

# View resolution only
DEBUG=express:view node app.js
```

### Graceful Shutdown

```javascript
const server = app.listen(3000);

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Server shut down gracefully');
    process.exit(0);
  });
});
```

> [!NOTE]
> `app.listen()` returns a standard `http.Server` instance. Use `server.close()` for graceful shutdown — it stops accepting new connections and waits for existing connections to complete.

### Monitoring

Key metrics to monitor for Express applications:

| Metric | How to Collect |
|---|---|
| Request rate | Custom middleware counter or APM tool |
| Response latency | `morgan` response-time token or custom middleware using `process.hrtime()` |
| Error rate | Error-handling middleware counter |
| Event loop lag | `perf_hooks.monitorEventLoopDelay()` |
| Memory usage | `process.memoryUsage()` |
| Active connections | `server.getConnections()` |

## 6. Performance

### ETag Optimization

Express generates ETags for every response by default (weak ETags). This enables client-side caching with `304 Not Modified` responses. The freshness check (`req.fresh`) compares `If-None-Match` / `If-Modified-Since` headers against the response's `ETag` / `Last-Modified`.

| ETag Mode | Trade-off |
|---|---|
| `'weak'` (default) | Good balance — allows semantic equivalence |
| `'strong'` | Byte-for-byte match required — stricter but compatible with range requests |
| `false` | Disables ETag generation — saves CPU for responses where caching is not useful |

### View Caching

In production (`NODE_ENV=production`), Express caches resolved view paths. The first `res.render('index')` call resolves the full filesystem path; subsequent calls use the cached path, skipping `fs.statSync` lookups.

This is automatic. To force caching in development for benchmarking:

```javascript
app.enable('view cache');
```

### JSON Serialization

For high-throughput JSON APIs, the `json escape` setting adds overhead by replacing `<`, `>`, `&` characters. Only enable it when embedding JSON in HTML contexts:

```javascript
// Only if you serve JSON inside <script> tags
app.set('json escape', true);
```

### Static File Serving

`express.static()` delegates to `serve-static`, which uses `send` for file streaming with:
- `fs.createReadStream()` for efficient file transfer
- Automatic `Content-Type` from file extension
- `ETag` and `Last-Modified` headers for caching
- `Range` request support for partial content

For production, consider serving static files directly from nginx or a CDN rather than through Express.

## 7. Security

### X-Powered-By Header

Express sends `X-Powered-By: Express` by default. This reveals the framework to attackers. Disable it in production:

```javascript
app.disable('x-powered-by');
```

> [!CAUTION]
> Leaving `x-powered-by` enabled discloses your technology stack. While security-through-obscurity alone is insufficient, removing framework fingerprints is a low-cost hardening measure.

### Trust Proxy Misconfiguration

Incorrect `trust proxy` settings can allow attackers to spoof their IP address via the `X-Forwarded-For` header:

- `trust proxy: true` trusts **all** proxy headers — an attacker can set `X-Forwarded-For: 127.0.0.1` and bypass IP-based access controls
- `trust proxy: false` (default) ignores forwarded headers — safe but `req.ip` returns the proxy's IP, not the client's

**Recommended**: set to the exact number of proxy hops or specific proxy IP addresses.

### JSONP Callback Sanitization

`res.jsonp()` sanitizes the callback parameter by stripping characters outside `[\w$.\[\]]` to prevent XSS injection through callback names. It also sets `X-Content-Type-Options: nosniff` to prevent MIME-type sniffing.

The response wraps JSON in a Rosetta Flash mitigation pattern:

```javascript
/**/ typeof callback === 'function' && callback({"data":"value"});
```

### Cookie Security

- `res.cookie()` defaults `path` to `'/'` if not specified
- `maxAge` is in milliseconds (converted to seconds in the `Set-Cookie` header)
- Signed cookies require `cookie-parser` middleware with a secret
- Object values are serialized as `j:` + JSON
- Signed values are prefixed with `s:` + HMAC signature

> [!WARNING]
> The cookie `path` defaults to `'/'`, meaning cookies are sent on all paths by default. For sensitive cookies (e.g., admin sessions), restrict the path to the relevant route prefix.

### JSON Escape

When `json escape` is enabled, `res.json()` and `res.jsonp()` replace `<` (`\u003c`), `>` (`\u003e`), and `&` (`\u0026`) in JSON output. This prevents HTML injection when JSON is embedded in `<script>` tags.

### Redirect Security

`res.redirect()` escapes HTML entities in the redirect URL when generating the HTML response body, preventing XSS through redirect targets. The `Location` header value is URL-encoded via `encodeUrl()`.

> [!WARNING]
> `res.location()` does not validate the URL. A redirect to user-supplied input without validation enables open redirect attacks. Always validate redirect targets against an allowlist.

### View Path Traversal

The `View.prototype.lookup()` method resolves template paths within the configured `views` directory using `path.resolve()`. However, it does not enforce path containment — a view name like `../../etc/passwd` could escape the views directory. GitHub issue #7140 tracks this as a known gap (unlike `send`, which enforces containment for `res.sendFile()`).

> [!CAUTION]
> Never pass unsanitized user input as a view name to `res.render()`. Always validate or whitelist template names.

### Request Size Limits

The body-parser middleware (`express.json()`, `express.urlencoded()`) defaults to a 100KB request body limit. Configure it for your needs:

```javascript
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));
```

### Query String Prototype Pollution

The extended query parser passes `{ allowPrototypes: true }` to `qs.parse()`. This prevents `qs` from stripping properties inherited from `Object.prototype` in parsed query objects, which can be a concern in code that uses `hasOwnProperty` checks or iterates over query parameters without guarding against prototype properties.

## 8. Developer Internals

### Repository Structure

```text
expressjs/express/
├── index.js                    # Entry point: re-exports lib/express.js
├── lib/
│   ├── express.js              # createApplication() factory, middleware re-exports
│   ├── application.js          # App prototype (settings, routing, render, listen)
│   ├── request.js              # Request prototype extensions
│   ├── response.js             # Response prototype extensions
│   ├── utils.js                # Internal utilities (ETag/query/trust compilation)
│   └── view.js                 # View class (template lookup + render)
├── examples/                   # 20+ example applications
│   ├── hello-world/            # Minimal Express app
│   ├── auth/                   # Session-based authentication
│   ├── mvc/                    # MVC pattern with controller autoloading
│   ├── multi-router/           # API versioning with Router
│   ├── route-separation/       # Route file organization
│   ├── content-negotiation/    # Accept header handling
│   ├── web-service/            # REST API example
│   ├── error-pages/            # Custom error pages (404/500)
│   ├── static-files/           # Static file serving
│   ├── session/                # Sessions (in-memory + Redis)
│   ├── cookies/                # Cookie handling
│   ├── cookie-sessions/        # Cookie-based sessions
│   ├── downloads/              # File download
│   ├── ejs/                    # EJS template engine
│   ├── markdown/               # Markdown rendering
│   ├── params/                 # Route parameters
│   ├── resource/               # Resource-oriented routing
│   ├── route-map/              # Route mapping pattern
│   ├── route-middleware/       # Per-route middleware
│   ├── search/                 # Search with AJAX
│   ├── vhost/                  # Virtual hosts
│   ├── view-constructor/       # Custom view class
│   ├── view-locals/            # Template local variables
│   ├── online/                 # Online user counter
│   └── error/                  # Error handling
├── test/                       # Test suite
│   ├── app.*.js                # Application tests
│   ├── req.*.js                # Request property/method tests
│   ├── res.*.js                # Response method tests
│   ├── express.*.js            # Built-in middleware tests
│   ├── acceptance/             # Integration tests for examples
│   ├── support/                # Test utilities
│   └── fixtures/               # Test fixture files
├── package.json                # Package manifest
├── .eslintrc.yml               # ESLint configuration
└── .editorconfig               # Editor configuration
```

### Module Reference

#### `lib/express.js` — Factory & Exports

The `createApplication()` function:

1. Creates a function `app` that delegates to `app.handle(req, res, next)`
2. Mixes in `EventEmitter.prototype` for event support (e.g., `mount` event)
3. Mixes in the `application.js` prototype for all app methods
4. Creates `app.request` and `app.response` as prototype objects that will be set on incoming req/res
5. Calls `app.init()` to configure defaults

**Exported members:**

| Export | Value |
|---|---|
| `express()` (default) | `createApplication` function |
| `express.application` | Application prototype |
| `express.request` | Request prototype |
| `express.response` | Response prototype |
| `express.Router` | Router constructor (from `router` package) |
| `express.Route` | Route constructor (from `router` package) |
| `express.json` | `body-parser.json` |
| `express.urlencoded` | `body-parser.urlencoded` |
| `express.raw` | `body-parser.raw` |
| `express.text` | `body-parser.text` |
| `express.static` | `serve-static` |

#### `lib/application.js` — Application Prototype

**Key methods:**

| Method | Visibility | Description |
|---|---|---|
| `init()` | Private | Initialize settings, lazy router getter, defaults |
| `defaultConfiguration()` | Private | Set defaults: `x-powered-by`, `etag`, `env`, `query parser`, `subdomain offset`, `trust proxy`, `views`, `view`, `jsonp callback name`; register `mount` event handler for inheritance |
| `handle(req, res, callback)` | Private | Request dispatch: sets headers, cross-references, prototypes, delegates to `router.handle()` |
| `use(fn)` | Public | Mount middleware or sub-application |
| `route(path)` | Public | Create a `Route` for the given path |
| `engine(ext, fn)` | Public | Register template engine |
| `param(name, fn)` | Public | Parameter pre-processing |
| `set(setting, [val])` | Public | Get/set application setting (triggers compilation for `etag`, `query parser`, `trust proxy`) |
| `enabled(setting)` / `disabled(setting)` | Public | Boolean setting checks |
| `enable(setting)` / `disable(setting)` | Public | Set boolean settings |
| `all(path)` | Public | Register handler for all HTTP methods |
| `render(name, options, callback)` | Public | Render a view |
| `listen()` | Public | Create `http.Server` and listen |
| `path()` | Private | Compute absolute mount path |

**HTTP method registration:**

```javascript
methods.forEach(function (method) {
  app[method] = function (path) {
    if (method === 'get' && arguments.length === 1) {
      return this.set(path);  // app.get(setting)
    }
    var route = this.route(path);
    route[method].apply(route, slice.call(arguments, 1));
    return this;
  };
});
```

This creates `app.get()`, `app.post()`, `app.put()`, `app.delete()`, `app.patch()`, `app.options()`, `app.head()`, etc. — one for every HTTP method that Node.js supports. Note the special `app.get()` dual behavior: with one argument it reads a setting, with two or more it defines a route.

**Router lazy initialization:**

The router is created on first access via `Object.defineProperty` getter:

```javascript
Object.defineProperty(this, 'router', {
  configurable: true,
  enumerable: true,
  get: function getrouter() {
    if (router === null) {
      router = new Router({
        caseSensitive: this.enabled('case sensitive routing'),
        strict: this.enabled('strict routing')
      });
    }
    return router;
  }
});
```

> [!NOTE]
> The Router's `caseSensitive` and `strict` options are set at construction time from the current application settings. Changing `case sensitive routing` or `strict routing` after routes have been registered will not affect existing routes.

**Sub-application mounting (`app.use()`):**

When a mounted argument has both `handle` and `set` methods (i.e., it's an Express app), `app.use()`:
1. Sets `fn.mountpath` to the mount path
2. Sets `fn.parent` to the parent app
3. Wraps `fn.handle()` in a middleware that saves and restores `req`/`res` prototypes
4. Emits `mount` on the child app

#### `lib/request.js` — Request Prototype

Extends `http.IncomingMessage.prototype` via `Object.create()`. All properties/methods are added directly to the prototype object.

**Getter properties** (defined via `defineGetter()`):

| Property | Logic |
|---|---|
| `query` | Calls the compiled `query parser fn` on `parseurl(this).query` |
| `protocol` | Returns `'https'` if socket is encrypted, else `'http'`. When trust proxy is set, reads `X-Forwarded-Proto` |
| `secure` | `this.protocol === 'https'` |
| `ip` | `proxyaddr(this, trustFn)` |
| `ips` | All proxy addresses via `proxyaddr.all()`, reversed, socket address removed |
| `subdomains` | Host split by `.`, reversed, sliced by `subdomain offset` |
| `path` | `parseurl(this).pathname` |
| `host` | Host header; when trust proxy is set, reads `X-Forwarded-Host` |
| `hostname` | `host` with port stripped (IPv6-aware) |
| `fresh` | `fresh(req.headers, { etag, last-modified })` for GET/HEAD with 2xx/304 |
| `stale` | `!this.fresh` |
| `xhr` | `X-Requested-With` header equals `'xmlhttprequest'` (case-insensitive) |

**Methods:**

| Method | Description |
|---|---|
| `get(name)` / `header(name)` | Return header value. Special-cases `Referrer`/`Referer` to check both. Throws if `name` is missing or not a string. |
| `accepts(types)` | Content negotiation via `accepts` package |
| `acceptsEncodings(encodings)` | Encoding negotiation |
| `acceptsCharsets(...charsets)` | Charset negotiation |
| `acceptsLanguages(...languages)` | Language negotiation |
| `range(size, [options])` | Parse Range header via `range-parser` |
| `is(types)` | Check Content-Type via `type-is` |

#### `lib/response.js` — Response Prototype

Extends `http.ServerResponse.prototype`. This is the largest core module (~2300 lines).

**Key methods:**

| Method | Key Behavior |
|---|---|
| `status(code)` | Validates: must be integer, 100–999. Throws `TypeError` or `RangeError` otherwise. |
| `send(body)` | String → sets Content-Type to HTML if not set. Object/Array → delegates to `json()`. Buffer → sets type to `bin`. Generates ETag if enabled. Strips body for 204/304. Sets Content-Length to 0 for 205. Checks `req.fresh` for 304. |
| `json(obj)` | Respects `json escape`, `json replacer`, `json spaces` settings. Sets Content-Type to `application/json`. |
| `jsonp(obj)` | Like `json()` but wraps in JSONP callback. Sanitizes callback name (strips non-`[\w$.\[\]]`). Rosetta Flash mitigation. Sets `X-Content-Type-Options: nosniff`. |
| `sendFile(path, opts, cb)` | Path must be absolute or `opts.root` must be set. Creates a `send` stream. Wires `etag` from app settings. |
| `download(path, name, opts, cb)` | Sets `Content-Disposition` via `content-disposition`. Resolves path if no `root` option. Delegates to `sendFile()`. |
| `redirect([status], url)` | Default status 302. Sets Location header. Sends HTML or text body via `res.format()`. Deprecation warnings for missing URL, non-string URL, non-number status. |
| `render(view, opts, cb)` | Merges `res.locals` into options as `_locals`. Defaults callback to `self.send(str)`. Delegates to `app.render()`. |
| `cookie(name, val, opts)` | Object values get `j:` prefix + JSON. Signed values get `s:` + HMAC. `maxAge` (ms) → `expires` (Date) + `maxAge` (seconds). Default path: `'/'`. |
| `clearCookie(name, opts)` | Sets `expires` to `new Date(1)` (epoch + 1ms). Removes `maxAge`. |
| `format(obj)` | Content negotiation: matches Accept header against provided MIME types/extensions. Adds `Vary: Accept`. 406 error if no match and no `default` callback. |

#### `lib/utils.js` — Internal Utilities

| Export | Purpose |
|---|---|
| `methods` | Lowercased list of HTTP methods from `http.METHODS` |
| `etag` / `wetag` | Strong/weak ETag generator functions |
| `normalizeType(type)` | Convert extension to MIME type with quality params |
| `normalizeTypes(types)` | Batch version of `normalizeType` |
| `compileETag(val)` | Compile `etag` setting to function |
| `compileQueryParser(val)` | Compile `query parser` setting to function |
| `compileTrust(val)` | Compile `trust proxy` setting to function |
| `setCharset(type, charset)` | Set charset parameter in Content-Type string |

#### `lib/view.js` — View Class

The `View` constructor:

1. Extracts file extension from the view name (or uses `defaultEngine`)
2. Auto-requires the template engine by extension and looks for `__express` export
3. Caches the engine in the shared `engines` object
4. Resolves the full path via `lookup()` → checks `<root>/<name>` then `<root>/<name>/index.<ext>`

`View.prototype.render()` normalizes synchronous engine callbacks by deferring them via `process.nextTick()` to ensure consistent async behavior.

`View.prototype.resolve()` uses `fs.statSync()` to check if the resolved path is a file. This is a synchronous filesystem call on every uncached render.

### Code Patterns and Conventions

**Prototype-based architecture:** Express does not use ES6 classes. All modules export plain objects that serve as prototypes, which are mixed into the app/req/res via `Object.setPrototypeOf()` or `Object.create()`.

**Chainable API:** Most `app.*` and `res.*` methods return `this` for method chaining.

**Dual-purpose methods:**
- `app.get()`: one arg = get setting, two+ args = define GET route
- `res.set()` / `res.header()`: two args = set one header, one object arg = set multiple headers

**Error creation:** Uses `http-errors` package for HTTP errors (e.g., 406 in `res.format()`). Direct `Error`/`TypeError`/`RangeError` for validation errors.

**Deprecation:** Uses `depd` package for deprecation warnings (e.g., in `res.redirect()` for missing URL arguments).

### Error Messages (Complete)

| Error Type | Message | Source | Trigger |
|---|---|---|---|
| `TypeError` | `app.use() requires a middleware function` | `lib/application.js:213` | No function argument passed to `app.use()` |
| `Error` | `callback function required` | `lib/application.js:296` | `app.engine()` called with non-function callback |
| `TypeError` | `name argument is required to req.get` | `lib/request.js:66` | `req.get()` called without argument |
| `TypeError` | `name must be a string to req.get` | `lib/request.js:70` | `req.get()` called with non-string |
| `TypeError` | `Invalid status code: ${code}. Status code must be an integer.` | `lib/response.js:67` | Non-integer passed to `res.status()` |
| `RangeError` | `Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` | `lib/response.js:71` | Status code outside 100–999 |
| `TypeError` | `path argument is required to res.sendFile` | `lib/response.js:379` | `res.sendFile()` called without path |
| `TypeError` | `path must be a string to res.sendFile` | `lib/response.js:383` | `res.sendFile()` called with non-string path |
| `TypeError` | `path must be absolute or specify root to res.sendFile` | `lib/response.js:393` | Relative path without `root` option |
| `TypeError` | `Content-Type cannot be set to an Array` | `lib/response.js:674` | `res.set('Content-Type', [...])` called with array |
| `Error` | `cookieParser("secret") required for signed cookies` | `lib/response.js:748` | Signed cookie without cookie-parser secret |
| `TypeError` | `unknown value for etag function: ${val}` | `lib/utils.js:148` | Invalid `etag` setting value |
| `TypeError` | `unknown value for query parser function: ${val}` | `lib/utils.js:180` | Invalid `query parser` setting value |
| `Error` | `No default engine was specified and no extension was provided.` | `lib/view.js:61` | `res.render('name')` without `view engine` set and no file extension |
| `Error` | `Module "${mod}" does not provide a view engine.` | `lib/view.js:84` | Template engine module lacks `__express` export |

### Testing Strategy

**Framework:** Mocha with supertest for HTTP assertions, nyc for coverage.

**Test scripts:**

| Script | Command |
|---|---|
| `npm test` | `mocha --require test/support/env --reporter spec --check-leaks test/ test/acceptance/` |
| `npm run test-cov` | Same tests with nyc HTML + text coverage report |
| `npm run test-ci` | Same tests with nyc lcov + text coverage (CI output) |
| `npm run test-tap` | Same tests with TAP reporter |

**Test organization:**

| Directory/Pattern | Coverage |
|---|---|
| `test/app.*.js` | Application methods: `use`, `set`, `engine`, `param`, `render`, `listen`, `route`, `all`, `head`, `options`, `router`, `routes.error` |
| `test/req.*.js` | Request properties: `accepts`, `acceptsCharsets`, `acceptsEncodings`, `acceptsLanguages`, `baseUrl`, `fresh`, `get`, `host`, `hostname`, `ip`, `ips`, `is`, `path`, `protocol`, `query`, `range`, `route`, `secure`, `signedCookies`, `stale`, `subdomains`, `xhr` |
| `test/res.*.js` | Response methods: `append`, `attachment`, `clearCookie`, `cookie`, `download`, `format`, `get`, `json`, `jsonp`, `links`, `locals`, `location`, `redirect`, `render`, `send`, `sendFile`, `sendStatus`, `set`, `status`, `type`, `vary` |
| `test/express.*.js` | Built-in middleware: `json`, `raw`, `static`, `text`, `urlencoded` |
| `test/config.js` | Application settings |
| `test/exports.js` | Module exports |
| `test/Route.js` / `test/Router.js` | Routing |
| `test/regression.js` | Regression tests |
| `test/middleware.basic.js` | Basic middleware behavior |
| `test/acceptance/*.js` | Integration tests for all 18 example applications |

**Test environment:** `test/support/env.js` sets `NODE_ENV=test` and `NO_DEPRECATION=express` to suppress deprecation warnings during tests.

> [!TIP]
> Run `DEBUG=express:* npm test` to see Express internals during test execution. The `--check-leaks` flag in the test command detects global variable leaks.

### Potential Improvements

Based on the codebase and community issues:

1. **View path containment** (GitHub #7140): `View.prototype.lookup()` does not enforce that resolved paths stay within the `views` directory, unlike `send` (used by `res.sendFile()`). This is a potential path traversal risk if user input reaches `res.render()`.

2. **Synchronous view resolution**: `View.prototype.resolve()` uses `fs.statSync()`, blocking the event loop during template lookup on uncached renders. An async alternative would improve performance under high concurrency.

3. **BigInt JSON serialization** (GitHub #7186, closed): `res.json()` throws a cryptic `TypeError` when the response body contains BigInt values. A more descriptive error message would improve developer experience.

4. **Double-slash mount paths** (GitHub #4557): `app.use('//', handler)` does not behave as expected. This is a known router-level issue.

5. **`allowPrototypes: true` in qs**: The extended query parser passes `{ allowPrototypes: true }` to `qs.parse()`. While this improves compatibility, it can be a concern in security-sensitive contexts where prototype pollution matters.

### Contributing

**Linting:**

```bash
npm run lint        # check
npm run lint:fix    # auto-fix
```

**Running tests:**

```bash
npm test            # full test suite
npm run test-cov    # with coverage report
```

**Code style:** ESLint configuration in `.eslintrc.yml`. EditorConfig in `.editorconfig`.

**License:** MIT

## 9. Integration Points

### Template Engines

Express integrates with any template engine that exposes a `__express(filePath, options, callback)` function. The following engines work out of the box:

| Engine | Extension | Install |
|---|---|---|
| EJS | `.ejs` | `npm install ejs` |
| Pug (Jade) | `.pug` | `npm install pug` |
| Handlebars | `.hbs` | `npm install hbs` |
| Mustache | `.mustache` | `npm install mustache-express` |

For engines without `__express`, use `app.engine()`:

```javascript
app.engine('md', (filePath, options, callback) => {
  const content = fs.readFileSync(filePath, 'utf8');
  const html = marked.parse(content);
  callback(null, html);
});
```

### Session Stores

Express works with `express-session` and any compatible session store:

```javascript
const session = require('express-session');
const RedisStore = require('connect-redis').default;

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'secret',
  resave: false,
  saveUninitialized: false
}));
```

### Common Middleware Ecosystem

| Middleware | Purpose |
|---|---|
| `cors` | Cross-Origin Resource Sharing |
| `helmet` | Security headers |
| `compression` | Gzip/deflate response compression |
| `morgan` | HTTP request logger |
| `cookie-parser` | Cookie parsing |
| `express-session` | Server-side sessions |
| `passport` | Authentication strategies |
| `multer` | Multipart form data (file uploads) |
| `express-rate-limit` | Rate limiting |
| `method-override` | HTTP method override via header/query |

### HTTP/HTTPS Server Integration

Express apps are standard Node.js request handlers:

```javascript
const http = require('node:http');
const https = require('node:https');
const app = express();

// HTTP
http.createServer(app).listen(80);

// HTTPS
https.createServer({ key, cert }, app).listen(443);

// Both on same app
```

`app.listen(port)` is shorthand for `http.createServer(app).listen(port)`. The returned `http.Server` instance supports all standard server events and methods.

---

_Generated May 2026 from expressjs/express at 6340c1e. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
