RepoFold

Application

The Express application object is the central orchestrator of an Express-based web server. It provides the top-level API for configuring behavior, mounting middleware and routes, and starting an HTTP server. This page documents how to create, configure, and manage the application object, covering all settings, lifecycle methods, and the mechanisms for composing middleware and sub-applications.

Purpose

The application object serves as the entry point for every Express project. It encapsulates the entire middleware pipeline, route definitions, view engine configuration, and server lifecycle. Rather than exposing a class that users instantiate directly, Express exports a factory function (express()) that produces a new application instance. This design keeps the API simple and avoids requiring new keywords.

The application object is also a Request and Response handler itself: it is a function that accepts Node.js req and res objects and processes them through its internal middleware stack. This property is what allows an Express application to be passed directly to http.createServer or mounted as a sub-application inside another Express app.

How It Works (Architecture of the Module)

The application module lives in lib/application.js and exports a single function that constructs an application object. The module depends on several internal components:

  • Router (from lib/router/index.js), provides the routing and middleware dispatch mechanism
  • methods (from Node.js http module), list of HTTP verbs used to define route methods
  • middleware/init, initializes the req and res objects with Express-specific properties
  • middleware/query, parses query strings from URLs
  • View (from lib/view.js), renders template files for the view system
  • utils.compileETag and utils.merge, utility functions for ETag generation and object merging

The application object is created by calling the top-level express() function, which is defined in index.js and delegates to lib/application.js. The constructor function createApplication sets up the initial state:

// lib/application.js, lines 48-63
var app = function(req, res, next) {
 app.handle(req, res, next);
};

// mixin EventEmitter
mixin(app, EventEmitter.prototype, 3);

// mixin methods from proto
mixin(app, proto, 3);

// expose the prototype that will get set on requests
app.request = Object.create(req, {
 app: { configurable: true, enumerable: true, writable: true, value: app }
});

// expose the prototype that will get set on responses
app.response = Object.create(res, {
 app: { configurable: true, enumerable: true, writable: true, value: app }
});

app.init();

The resulting app object is a function that, when called, invokes app.handle. It also inherits from EventEmitter (for emitting events like 'mount') and from the proto object, which carries all the methods like use, get, set, listen, and render.

The init() method (lines 65-80) sets default properties:

app.cache = {};
app.engines = {};
app.settings = {};

app.defaultConfiguration();

defaultConfiguration (lines 82-107) populates the settings object with defaults:

this.enable('x-powered-by');
this.set('etag', 'weak');
this.set('env', env);
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
this.set('views', root + '/views');
this.set('view cache', false);
this.set('view engine');

These settings control everything from ETag behavior to template resolution. Each setting can be overridden with app.set(name, value).

Walkthrough of Each Main Flow

Creating an Application

To create an Express application, call the exported function:

var express = require('express');
var app = express();

This invokes createApplication in lib/application.js (line 48). The returned app object is immediately ready for configuration. No additional initialization is required.

Configuring Settings with app.set() and app.get()

The app.set(name, value) method stores a value in app.settings and returns the app for chaining. app.get(name) retrieves the value. These are defined in lib/application.js:

// lines 109-122
app.set = function set(setting, val) {
 if (arguments.length === 1) {
 // getter
 return this.settings[setting];
 }

 // setter
 this.settings[setting] = val;

 // special handling for "trust proxy" and "etag"
 switch (setting) {
 case 'etag':
 this.set('etag fn', compileETag(val));
 break;
 case 'trust proxy':
 this.set('trust proxy fn', compileTrust(val));
 break;
 case 'query parser':
 this.set('query parser fn', compileQueryParser(val));
 break;
 }

 return this;
};

When setting etag, trust proxy, or query parser, Express compiles the value into a function and stores it under a separate key (etag fn, trust proxy fn, query parser fn). This precompilation avoids repeated parsing on every request.

The app.enable(name) and app.disable(name) methods are shorthand for app.set(name, true) and app.set(name, false). Similarly, app.enabled(name) and app.disabled(name) are boolean getters.

Mounting Middleware and Sub-Applications with app.use()

The app.use() method is the primary way to mount middleware functions or sub-applications. It is defined in lib/application.js (lines 124-148):

app.use = function use(fn) {
 var offset = 0;
 var path = '/';

 // default path to '/'
 // disambiguate app.use([fn])
 if (typeof fn!== 'function') {
 var arg = fn;

 while (Array.isArray(arg) && arg.length!== 0) {
 arg = arg[0];
 }

 // first arg is the path
 if (typeof arg!== 'function') {
 offset = 1;
 path = fn;
 }
 }

 var fns = flatten(slice.call(arguments, offset));

 if (fns.length === 0) {
 throw new TypeError('app.use() requires a middleware function');
 }

 // setup router
 this.lazyrouter();
 var router = this._router;

 fns.forEach(function (fn) {
 // non-express app
 if (!fn ||!fn.handle ||!fn.set) {
 return router.use(path, fn);
 }

 // sub-app
 fn._parent = this;
 fn.emit('mount', this);
 router.use(path, function mountApplication(req, res, next) {
 var app = fn;
 app.handle(req, res, next);
 });
 }, this);

 return this;
};

Key behaviors:

  1. Path detection: If the first argument is not a function (or an array of functions), it is treated as a mount path. The default path is '/'.
  2. Flattening: Middleware arguments are flattened, so app.use(fn1, [fn2, fn3]) works as expected.
  3. Lazy router initialization: this.lazyrouter() creates the internal _router (a Router instance) on first use. This avoids allocating a router for applications that only use app.set and app.listen.
  4. Sub-application detection: If a middleware has a .handle and .set property, it is treated as a sub-application. The sub-application's _parent is set to the parent, and a 'mount' event is emitted. The sub-application is mounted by wrapping its handle method in a middleware function.
  5. Regular middleware: Functions without .handle/.set are passed directly to router.use(path, fn).

The lazyrouter method (lines 150-165) creates the router if it does not exist:

app.lazyrouter = function lazyrouter() {
 if (!this._router) {
 this._router = new Router({
 caseSensitive: this.enabled('case sensitive routing'),
 strict: this.enabled('strict routing')
 });

 this._router.use(query(this.get('query parser fn')));
 this._router.use(middleware.init(this));
 }
};

Notice that the first two middleware functions added to every router are the query parser and the init middleware. The query parser parses the URL query string into req.query. The init middleware sets up req.app, res.app, req.res, res.req, and other Express-specific properties.

Defining Routes

Express provides convenience methods for each HTTP verb (get, post, put, delete, patch, etc.). These are generated dynamically in lib/application.js (lines 167-185):

methods.forEach(function(method){
 app[method] = function(path){
 if (method === 'get' && arguments.length === 1) {
 // app.get(setting)
 return this.set(path);
 }

 this.lazyrouter();

 var route = this._router.route(path);
 route[method].apply(route, slice.call(arguments, 1));
 return this;
 };
});

The methods array comes from Node.js http.METHODS. For each method, a function is added to the app prototype. When called with a path and handler(s), it:

  1. Calls lazyrouter() to ensure the router exists.
  2. Creates a new Route object via this._router.route(path).
  3. Calls the corresponding method on the route (e.g., route.get(handler)).
  4. Returns the app for chaining.

There is a special case for get: if called with a single argument, it acts as a getter for settings (delegating to app.set(path)). This allows app.get('some-setting') to retrieve a setting value.

Starting the Server with app.listen()

The app.listen() method is defined in lib/application.js (lines 187-197):

app.listen = function listen() {
 var server = http.createServer(this);
 return server.listen.apply(server, arguments);
};

This is a thin wrapper around Node.js http.createServer. Because the application object is itself a function (it is the handle method), it can be passed directly to http.createServer. The listen method forwards all arguments (port, host, backlog, callback) to server.listen.

For HTTPS support, users create their own server:

var https = require('https');
var server = https.createServer(options, app);
server.listen(443);

Rendering Views with app.render()

The app.render(name, options, callback) method renders a template file. It is defined in lib/application.js (lines 199-253):

app.render = function render(name, options, callback) {
 var cache = this.cache;
 var done = callback;
 var engines = this.engines;
 var opts = options;
 var renderOptions = {};
 var view;

 // support callback as second arg
 if (typeof options === 'function') {
 done = options;
 opts = {};
 }

 // merge app.locals
 merge(renderOptions, this.locals);

 // merge options._locals
 if (opts._locals) {
 merge(renderOptions, opts._locals);
 }

 // merge options
 merge(renderOptions, opts);

 // set.cache
 if (renderOptions.cache == null) {
 renderOptions.cache = this.enabled('view cache');
 }

 // handle view existence
 if (typeof name!== 'string') {
 throw new Error('View name must be a string');
 }

 view = new View(name, {
 defaultEngine: this.get('view engine'),
 root: this.get('views'),
 engines: engines
 });

 if (!view.path) {
 var dirs = Array.isArray(view.root) && view.root.length > 1
? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
: 'directory "' + view.root + '"';
 var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
 err.view = view;
 return done(err);
 }

 tryRender(view, renderOptions, done);
};

The method merges locals in this order (later overrides earlier):

  1. app.locals (application-level locals)
  2. options._locals (if provided)
  3. options (the explicit options passed to render)

It then creates a View object, which resolves the template file path using the configured views directory and view engine. If the view is not found, an error is returned. Otherwise, tryRender calls the view engine's render function.

Handling Requests with app.handle()

The app.handle(req, res, callback) method is the core request handler. It is defined in lib/application.js (lines 255-280):

app.handle = function handle(req, res, callback) {
 var router = this._router;

 // final handler
 var done = callback || finalhandler(req, res, {
 env: this.get('env'),
 onerror: logerror.bind(this)
 });

 // no routes
 if (!router) {
 done();
 return;
 }

 router.handle(req, res, done);
};

If no router has been created (because no routes or middleware were mounted), the request falls through to the finalhandler, which produces a 404 response. Otherwise, router.handle dispatches the request through the middleware stack.

The finalhandler module (from npm) is used as the default error handler. It respects the env setting: in development, it includes stack traces; in production, it returns a generic error page.

Public API

Application Creation

SymbolSignaturePurpose
express()express()Creates a new application instance

Settings

SymbolSignaturePurpose
app.set(name, value)set(string, any): appSets a setting value; returns app for chaining
app.get(name)get(string): anyGets a setting value (or route handler if called as app.get(path, handler))
app.enable(name)enable(string): appSets a boolean setting to true
app.disable(name)disable(string): appSets a boolean setting to false
app.enabled(name)enabled(string): booleanReturns true if the setting is truthy
app.disabled(name)disabled(string): booleanReturns true if the setting is falsy

Middleware and Routing

SymbolSignaturePurpose
app.use([path], fn)`use(string?, FunctionArray): app`
app.METHOD(path, handler...)METHOD(string, Function...): appDefines a route for HTTP method (get, post, put, etc.)
app.all(path, handler...)all(string, Function...): appDefines a route that matches all HTTP methods
app.param(name, fn)param(string, Function): appAdds a parameter middleware for route parameters
app.route(path)route(string): RouteReturns a Route instance for chaining multiple methods

Server Lifecycle

SymbolSignaturePurpose
app.listen(port, host?, backlog?, callback?)listen(...any): ServerCreates an HTTP server and starts listening
app.handle(req, res, callback?)handle(IncomingMessage, ServerResponse, Function?)Processes a request through the middleware stack

View System

SymbolSignaturePurpose
app.render(view, options?, callback?)render(string, Object?, Function): undefinedRenders a template with given options
app.engine(ext, fn)engine(string, Function): appRegisters a template engine for a file extension

Locals

SymbolSignaturePurpose
app.localsObjectApplication-level locals object, available in all rendered views

Events

EventPayloadDescription
'mount'parentAppEmitted when the application is mounted as a sub-application on a parent

Interactions with Other Modules

Router

The application delegates all routing and middleware dispatch to the Router object stored in app._router. The router is created lazily on first use of app.use, app.METHOD, or app.route. The router's handle method is called by app.handle for each incoming request.

Request and Response

The application sets up the prototypes for req and res objects. In createApplication, app.request and app.response are created as objects that inherit from Node.js http.IncomingMessage.prototype and http.ServerResponse.prototype respectively, with an app property pointing back to the application. The init middleware (added during lazyrouter) copies these prototypes onto each request and response object.

View System

The application stores view engine functions in app.engines and view configuration in settings (views, view engine, view cache). The app.render method creates a View object that resolves template files and calls the appropriate engine.

Sub-Applications

When a sub-application is mounted via app.use, the parent sets subApp._parent = this and emits a 'mount' event. The sub-application's handle method is wrapped in a middleware that passes requests through. This allows nested Express apps to share the same request/response lifecycle while maintaining separate middleware stacks and settings.

Configuration and Conventions

Available Settings

The following table lists all settings that Express recognizes. Settings are stored in app.settings and accessed via app.get(name).

SettingDefaultDescription
envprocess.env.NODE_ENV or 'development'Application environment; affects error handling and view caching
etag'weak'ETag response header generation; can be true, false, 'weak', 'strong', or a custom function
query parser'extended'Query string parser; can be 'extended' (qs), 'simple' (querystring), or false to disable
subdomain offset2Number of dot-separated parts to remove from the hostname to access subdomain
trust proxyfalseEnables reverse proxy support; can be true, false, IP addresses, or a custom function
viewsprocess.cwd() + '/views'Directory or array of directories where template files are located
view cachefalse (disabled in development)Enables template compilation caching
view engine(none)Default template engine extension (e.g., 'pug')
x-powered-bytrueWhether to include X-Powered-By: Express header
case sensitive routingundefined (disabled)When enabled, /Foo and /foo are treated as different routes
strict routingundefined (disabled)When enabled, /foo and /foo/ are treated as different routes

Conventions

  • Application-level locals: Use app.locals to store data that should be available in all rendered views (e.g., site title, user session data). This object is merged into the render options automatically.
  • Mounting order: Middleware and routes are executed in the order they are added with app.use or app.METHOD. Place error-handling middleware (with four arguments) last.
  • Sub-application isolation: Sub-applications have their own settings, middleware stack, and routes. They inherit the parent's env setting but can override it.
  • Environment-based behavior: The env setting controls whether stack traces are shown in error responses and whether view caching is enabled. Set NODE_ENV=production in production environments.
  • Lazy initialization: The internal router is not created until the first middleware or route is added. This means app.set and app.get calls before any app.use or app.get (route) do not allocate a router.
  • Overview, High-level introduction to Express
  • Architecture, How the framework is structured internally
  • Routing, Detailed documentation on route definitions and parameters
  • Middleware, How middleware functions work and how to write them
  • Request and Response, The req and res objects and their APIs
  • View System, Template engines and rendering
Generated from commit ae6dd37