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(fromlib/router/index.js), provides the routing and middleware dispatch mechanismmethods(from Node.jshttpmodule), list of HTTP verbs used to define route methodsmiddleware/init, initializes thereqandresobjects with Express-specific propertiesmiddleware/query, parses query strings from URLsView(fromlib/view.js), renders template files for the view systemutils.compileETagandutils.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:
- 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
'/'. - Flattening: Middleware arguments are flattened, so
app.use(fn1, [fn2, fn3])works as expected. - Lazy router initialization:
this.lazyrouter()creates the internal_router(aRouterinstance) on first use. This avoids allocating a router for applications that only useapp.setandapp.listen. - Sub-application detection: If a middleware has a
.handleand.setproperty, it is treated as a sub-application. The sub-application's_parentis set to the parent, and a'mount'event is emitted. The sub-application is mounted by wrapping itshandlemethod in a middleware function. - Regular middleware: Functions without
.handle/.setare passed directly torouter.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:
- Calls
lazyrouter()to ensure the router exists. - Creates a new
Routeobject viathis._router.route(path). - Calls the corresponding method on the route (e.g.,
route.get(handler)). - 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):
app.locals(application-level locals)options._locals(if provided)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
| Symbol | Signature | Purpose |
|---|---|---|
express() | express() | Creates a new application instance |
Settings
| Symbol | Signature | Purpose |
|---|---|---|
app.set(name, value) | set(string, any): app | Sets a setting value; returns app for chaining |
app.get(name) | get(string): any | Gets a setting value (or route handler if called as app.get(path, handler)) |
app.enable(name) | enable(string): app | Sets a boolean setting to true |
app.disable(name) | disable(string): app | Sets a boolean setting to false |
app.enabled(name) | enabled(string): boolean | Returns true if the setting is truthy |
app.disabled(name) | disabled(string): boolean | Returns true if the setting is falsy |
Middleware and Routing
| Symbol | Signature | Purpose |
|---|---|---|
app.use([path], fn) | `use(string?, Function | Array): app` |
app.METHOD(path, handler...) | METHOD(string, Function...): app | Defines a route for HTTP method (get, post, put, etc.) |
app.all(path, handler...) | all(string, Function...): app | Defines a route that matches all HTTP methods |
app.param(name, fn) | param(string, Function): app | Adds a parameter middleware for route parameters |
app.route(path) | route(string): Route | Returns a Route instance for chaining multiple methods |
Server Lifecycle
| Symbol | Signature | Purpose |
|---|---|---|
app.listen(port, host?, backlog?, callback?) | listen(...any): Server | Creates an HTTP server and starts listening |
app.handle(req, res, callback?) | handle(IncomingMessage, ServerResponse, Function?) | Processes a request through the middleware stack |
View System
| Symbol | Signature | Purpose |
|---|---|---|
app.render(view, options?, callback?) | render(string, Object?, Function): undefined | Renders a template with given options |
app.engine(ext, fn) | engine(string, Function): app | Registers a template engine for a file extension |
Locals
| Symbol | Signature | Purpose |
|---|---|---|
app.locals | Object | Application-level locals object, available in all rendered views |
Events
| Event | Payload | Description |
|---|---|---|
'mount' | parentApp | Emitted 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).
| Setting | Default | Description |
|---|---|---|
env | process.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 offset | 2 | Number of dot-separated parts to remove from the hostname to access subdomain |
trust proxy | false | Enables reverse proxy support; can be true, false, IP addresses, or a custom function |
views | process.cwd() + '/views' | Directory or array of directories where template files are located |
view cache | false (disabled in development) | Enables template compilation caching |
view engine | (none) | Default template engine extension (e.g., 'pug') |
x-powered-by | true | Whether to include X-Powered-By: Express header |
case sensitive routing | undefined (disabled) | When enabled, /Foo and /foo are treated as different routes |
strict routing | undefined (disabled) | When enabled, /foo and /foo/ are treated as different routes |
Conventions
- Application-level locals: Use
app.localsto 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.useorapp.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
envsetting but can override it. - Environment-based behavior: The
envsetting controls whether stack traces are shown in error responses and whether view caching is enabled. SetNODE_ENV=productionin production environments. - Lazy initialization: The internal router is not created until the first middleware or route is added. This means
app.setandapp.getcalls before anyapp.useorapp.get(route) do not allocate a router.
Related Pages
- 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
reqandresobjects and their APIs - View System, Template engines and rendering