expressjs/express
Patterns and conventions
Active contributors: ulisesgascon, jonchurch, wesleytodd
Express's coding style is older and tighter than most modern Node projects. New code should match the surrounding file rather than introduce a new dialect.
Module style
- CommonJS only. Files use
var foo = require('foo')andmodule.exports = .... No ES modules in source. - Strict mode. Every file begins with
'use strict';. - License banner. Each file starts with the standard banner naming TJ Holowaychuk, Roman Shtylman, and Douglas Christopher Wilson.
- JSDoc. Public methods have JSDoc with
@param,@return, and@public/@private/@apiannotations. Several response helpers have full usage examples in their JSDoc.
Variable declarations
vareverywhere. The codebase predateslet/constand is consistent in not using them, except in a few newer files whereconstis used for module-level constants (e.g., theBufferimport inlib/response.js).- The ESLint config does not forbid
let/const, but PRs that mix styles within a file are typically asked to align with the file.
Indentation and formatting
.eslintrc.yml:
- 2-space indent with
MemberExpression: offandSwitchCase: 1. eol-last: error(files end with a newline).no-trailing-spaces: error.eqeqeq: [error, allow-null]—===everywhere except== nullis permitted.no-unused-varswithargs: noneandignoreRestSiblings: true.no-restricted-globalsbans the globalBuffer. Useconst { Buffer } = require('node:buffer')(or theimportequivalent in tests).
Run npm run lint to check, npm run lint:fix to auto-fix.
Built-in modules
Use node: prefixes for built-ins: require('node:http'), require('node:path'), require('node:querystring'), etc. This was tightened up during the 5.x cleanup pass.
Error handling
- Throw
TypeErrorfor programmer errors (wrong argument types). Examples inlib/response.js:res.sendFilethrowsTypeErrorwhenpathis missing or not a string. - Throw
RangeErrorfor invalid numeric ranges (e.g.,res.statusoutside [100, 999]). - Use
http-errors(createError) for HTTP-status errors: e.g.,res.formatcallsnext(createError(406, ...))when content negotiation fails. - Pass errors through
next(err)in middleware-style functions.
Errors should include enough context to be debugged from logs: Failed to lookup view "${name}" in views ${dirs} is a good template.
Deprecation
lib/response.js uses the depd-style deprecate helper for soft deprecations (e.g., res.redirect warning when the URL is undefined). Internal-only deprecations are not announced to users; user-visible ones go in History.md.
Logging
Use the debug package, namespaced as express:<subsystem>:
var debug = require('debug')('express:application');
debug('booting in %s mode', env);Never console.log at production paths. The only console.error in core (logerror in lib/application.js) is for the default error handler, and it short-circuits in env === 'test'.
Prototypes over classes
Express predates ES classes and consistently uses prototype-based inheritance:
var req = Object.create(http.IncomingMessage.prototype)inlib/request.js.var res = Object.create(http.ServerResponse.prototype)inlib/response.js.- The
applicationprototype is a plain object:var app = exports = module.exports = {};inlib/application.js.
When extending, copy methods onto these prototypes via direct assignment (req.foo = function ...) or via merge-descriptors. Resist refactoring to ES classes — it would change the public extension contract.
Argument flexibility
Many helpers accept multiple call signatures (res.send(body), res.send(buffer), res.json(obj), res.cookie(name, value, opts), res.download(path[, filename][, opts][, cb])). The convention is:
- Detect the type of each argument with
typeofchecks. - Reassign locals to canonical positions.
- Run the body of the function against the canonical form.
res.download in lib/response.js is a representative example.
Performance-sensitive idioms
Array.prototype.slice.call(arguments, n)instead of rest parameters in some hot paths.Buffer.byteLength(chunk, encoding)for length-without-allocation.- ETag is only generated when there is no existing ETag and the body is small; otherwise
Buffer.fromis used to compute the length and the ETag in one pass. Object.create(null)for objects used as plain maps (e.g.,app.cache,app.engines,app.settings,req.querywhen parser is disabled).
File-level structure
A typical lib/*.js file is organised as:
- License banner.
'use strict';.// Module dependencies.block ofrequire()s.// Module variables.of internal helpers.- The exported prototype/object/factory.
- Method definitions, ordered by public surface first, then
@privatehelpers. - Internal helper functions at the bottom.
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.