Application and Settings
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Express 5.3 (npm express 5.3.0, published 2026-10-09) on Node.js 18 or later, the minimum
for Express 5, and it states where Express 4 (latest 4.22.3) behaves differently. Everything was checked against the
official documentation on 2026-10-29, and the examples were run against Express 5.3.0 on Node.js 24. The application
is the object returned by express(): it holds the settings that tune Express, the list of middleware and routes, the
local variables shared by every template, and the method that starts an HTTP server. Almost every other page in this
section configures something on it, so this page is the reference for what the application object is, what each setting
does, and how the application is turned into a running server. The examples belong to the Bookshelf API that grows
across the section, use ESM and the node: prefix for built-in modules, and contain placeholders only, never real
secrets.
What express() returns
express() is the function exported by the express package, and calling it creates an application. The object it
returns is itself a plain JavaScript function with the signature (req, res), with the application methods and the
settings store attached to it. That is why the same object can be handed to any HTTP server that accepts a request
listener, as http.createServer(app) does, and why app.listen() is only a convenience wrapper around that call.
The package exports more than the factory. These are the other members, covered on the page that owns each of them:
| Export | What it is | Page |
|---|---|---|
|
Creates a mountable mini-application with its own stack of middleware and routes. |
|
|
Request body parsers backed by |
|
|
Static file middleware backed by |
|
|
The prototypes from which every application, request and response inherits. Replacing a property on them changes Express itself. |
The factory express.createServer() of Express 2 and 3 was removed in Express 4, as was app.configure(). Old tutorials
that call either are describing an API that no longer exists; see Where the books are outdated.
An application does five jobs, and the rest of the page takes them in turn:
-
it stores settings (The settings API and Every application setting);
-
it stores template locals shared by every view (
app.locals); -
it keeps the ordered stack of middleware and routes, built by
app.use(),app.METHOD(),app.all(),app.route()andapp.param()(described in Middleware Fundamentals and Routing) and reachable asapp.router(app.router); -
it can be mounted inside another application (Sub-applications,
app.mountpathand themountevent); -
it renders views and turns itself into a server (
app.engine(),app.render()andapp.path()and Starting a server).
The settings API
Settings are a small key-value store that lives on app.settings. Express reads some keys itself (the table in
Every application setting); you can store your own keys beside them. Five methods work with the store:
| Method | Behaviour |
|---|---|
|
Stores the value and returns the application, so calls can be chained. For |
|
Returns the stored value. The same method name registers a route when it is called with a path and handlers, so Express tells them apart by argument count: one string argument is a settings read. |
|
Shortcuts for |
|
Return |
app.set(name) with a single argument is a synonym of app.get(name). Because the store is shared, avoid naming a
custom key like a built-in one. A common and perfectly valid use of the store is application-wide configuration read
from other modules through req.app.get(name), for example a page size limit; for anything larger than a handful of
values, a validated configuration module (see Project
Structure and Configuration) is easier to test.
The following module builds the Bookshelf application with the settings that every later page assumes. It exports the application without listening, which is the split that lets tests import it (see Starting a server).
// src/app.js
import { join } from 'node:path';
import express from 'express';
export function createApp({ env = process.env.NODE_ENV ?? 'development' } = {}) {
const app = express();
// Routing settings first: they are read when the router is created, on the first
// app.use() or app.METHOD() call, so set them before registering anything.
app.enable('strict routing');
app.disable('case sensitive routing');
// Security and fingerprinting.
app.disable('x-powered-by');
app.set('trust proxy', env === 'production' ? 1 : false);
// Request parsing and JSON output.
app.set('query parser', 'extended');
app.set('json spaces', env === 'production' ? 0 : 2);
// Views: an absolute path, not one that depends on the working directory.
app.set('views', join(import.meta.dirname, 'views'));
app.set('view engine', 'html');
// Custom keys live in the same store.
app.set('page size limit', 50);
app.get('/health', (req, res) => {
res.json({ status: 'ok', env: req.app.get('env'), limit: req.app.get('page size limit') });
});
return app;
}
import.meta.dirname exists from Node.js 20.11 and 21.2. On Node.js 18 use fileURLToPath(new URL('./views',
import.meta.url)) instead.
Every application setting
The table lists the settings that Express reads, with their default in Express 5.3 and what each one changes. The
defaults were confirmed by reading app.settings of a fresh application. The detailed sections after the table cover the
ones that need more than a line.
| Setting | Type | Default | Effect |
|---|---|---|---|
|
Boolean |
|
When enabled, |
|
String |
|
The environment name, read once when the application is created. Controls the default of |
|
Boolean, String or Function |
|
How the |
|
String |
|
The query-string parameter that |
|
Boolean |
|
Escapes |
|
Function, Array |
none |
The |
|
Number or String |
none |
The |
|
Boolean, String or Function |
|
How |
|
Boolean |
|
When enabled, |
|
Number |
|
How many dot-separated parts at the end of the host name to remove to get |
|
Boolean, String, Number, Array or Function |
|
Which reverse proxies are trusted to report the client address, protocol and host. See |
|
String or Array |
|
The directory, or array of directories, searched for view files. |
|
Boolean |
|
Caches the resolved view (and, for engines that support it, the compiled template) between renders. |
|
String |
none |
The default template engine extension, used when |
|
Boolean |
|
Sends the |
Reading app.settings of a fresh Express 5.3 application also shows some keys that are not in the table: etag fn,
query parser fn and trust proxy fn hold the compiled forms described above, and view holds the View class that
res.render() instantiates. Assigning a class to view replaces how views are looked up, which is the technique the
official view-constructor example uses to render from a source other than the file system (see
Template Engines and Views).
env
app.get('env') is the application’s idea of the environment. Express computes it once, in express(), from
process.env.NODE_ENV and falls back to development; setting process.env.NODE_ENV afterwards does not change it, but
app.set('env', 'production') does. Two behaviours depend on the value in Express itself: when it is production,
view cache is switched on, and the default error handler (from finalhandler) stops sending the stack trace to the
client. Many third-party libraries read NODE_ENV directly and ignore the Express setting, so set the environment
variable and let env follow it. The full effect of NODE_ENV=production is described in
Performance Best Practices.
case sensitive routing and strict routing
Both settings are passed to the router when it is created. Express creates the router lazily, on the first call of
app.use(), app.METHOD(), app.all() or any access to app.router, so changing either setting after a route has been
registered has no effect on the routes that already exist. Set them first, as the example above does, and set them again
on every sub-application and express.Router() that needs them: a router created with express.Router() takes its own
caseSensitive and strict options and does not read the application’s settings. The matching rules, and what
path-to-regexp 8 does with trailing slashes, are in
Route Paths and Parameters.
etag
When res.send(), res.json() or res.sendStatus() sends a body, Express generates an ETag header from it unless the
response already has one. The setting decides how:
-
trueorweak: a weak validator (the value starts withW/), the default; -
false: noETagis generated; -
strong: a strong validator, which promises that the bytes are identical; -
a function
(body, encoding)that receives the body as aBufferand the encoding, and returns the header value. This is the hook for a cheaper or content-aware hash, as in the example below.
Any other value throws a TypeError (unknown value for etag function) as soon as app.set('etag', value) runs. The
setting does not affect res.sendFile() or express.static(), which have their own etag option and take the validator
from the file’s size and modification time. How ETags drive conditional requests and 304 responses is the subject of
Content Negotiation and HTTP Caching.
The json settings and jsonp callback name
json spaces, json replacer and json escape configure how res.json() and res.jsonp() serialise a value, and
therefore every JSON response of the API at once. json spaces pretty-prints; leave it unset in production because it
makes every response larger. json replacer is the replacer of JSON.stringify(), a function or an array of
allowed keys, and works as a last line of defence that removes a field from every response. Do not treat it as the way to
keep secrets out of responses: shape the response explicitly, as described in
Validation and Input Handling. json escape replaces <, > and &
with the JSON escapes \u003c, \u003e and \u0026, which helps when a JSON response might be sniffed as HTML
or when the JSON is embedded in a page. jsonp callback name only matters for res.jsonp(), a legacy cross-origin
technique that CORS has replaced; do not build new endpoints on it.
// settings-demo.mjs
import express from 'express';
const app = express();
app.disable('x-powered-by');
app.set('json spaces', 2);
app.enable('json escape');
app.set('json replacer', (key, value) => (key === 'internalNote' ? undefined : value));
// Tag from the body length only, to show the hook. Prefer a real content hash.
app.set('etag', (body, encoding) => `"len-${Buffer.byteLength(body, encoding)}"`);
app.get('/books/1', (req, res) => {
res.json({ id: 1, title: 'Dune <1965>', internalNote: 'never public' });
});
const server = app.listen(3000, (error) => {
if (error) throw error;
console.log('listening on http://localhost:3000');
});
process.on('SIGINT', () => server.close());
GET /books/1 HTTP/1.1
Host: localhost:3000
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "len-49"
{
"id": 1,
"title": "Dune \u003c1965\u003e"
}
The internalNote field was removed by the replacer, the angle brackets were escaped, and the output is indented by two
spaces. The ETag value is computed by the custom function from the serialised body.
query parser
req.query is built from the query string by a parser chosen by this setting:
-
simple(the default in Express 5) uses Node.jsnode:querystring. A key is a flat string:filter[genre]=scifiproduces the keyfilter[genre]. Repeated keys become an array. -
extendeduses theqslibrary. Brackets build nested objects and arrays:filter[genre]=scifiproduces{ filter: { genre: 'scifi' } }. This was the default in Express 4. -
falsedisables parsing, andreq.queryis an empty object. Use it when you readreq.urlyourself. -
a function
(queryString) => objectplugs in your own parser.
// query-parser-demo.mjs
import { once } from 'node:events';
import express from 'express';
for (const mode of ['simple', 'extended']) {
const app = express();
app.set('query parser', mode);
app.get('/books', (req, res) => res.json(req.query));
const server = app.listen(0);
await once(server, 'listening');
const { port } = server.address();
const response = await fetch(`http://localhost:${port}/books?filter[genre]=scifi&tag=a&tag=b&x[]=1`);
console.log(mode, await response.text());
server.close();
}
simple {"filter[genre]":"scifi","tag":["a","b"],"x[]":"1"}
extended {"filter":{"genre":"scifi"},"tag":["a","b"],"x":["1"]}
Two consequences matter in practice. First, a client that sends nested query strings works on Express 4 and silently
stops working on Express 5 unless the application sets extended. Second, neither parser gives you a typed value: a
number arrives as a string, and a repeated key is an array while a single occurrence is a string, so a handler that
expects either must normalise. Validate and coerce req.query with a schema (see
Validation and Input Handling). In Express 5 req.query is a
getter that parses on access, so assigning to it no longer works; copy it into a new object instead.
trust proxy
Behind a load balancer or reverse proxy, the TCP peer of Express is the proxy, not the client. With trust proxy left at
false, req.ip is the proxy’s address, req.protocol is http even when the browser used HTTPS, and a cookie marked
secure is never sent. Enabling the setting makes Express read the X-Forwarded-For, X-Forwarded-Proto and
X-Forwarded-Host headers, but only from the proxies you name, because any client can forge those headers when the
connection does not come through a proxy you control. The accepted values are:
-
a Boolean:
truetrusts every hop and takes the left-most address (never do this on a server that can be reached directly);falsetrusts none; -
a number: the count of proxy hops in front of the application, for example
1behind a single load balancer; -
a string, comma-separated list or array of IP addresses and subnets, which may include the keywords
loopback,linklocalanduniquelocal; -
a function
(address, hopIndex) => booleanfor custom logic.
A sub-application with no explicit trust proxy of its own inherits the parent’s value. The full explanation, with the
values for common platforms and the effect on req.ip, req.ips, req.protocol, req.hostname and req.secure, is in
Behind Proxies; the cookie consequences are in
Cookies and Sessions.
subdomain offset
req.subdomains is the host name’s labels in reverse order, after removing the last subdomain offset labels. With the
default of 2, the host tobi.ferrets.example.com yields ['ferrets', 'tobi']. Set it to 3 for a host such as
tobi.ferrets.example.co.uk, because the registrable domain has three labels. The setting only changes how the host
header is split; it does not validate the host name, which is client-controlled.
views, view cache and view engine
views is where res.render() and app.render() look for templates. The default is a views directory under the
process working directory, not under the file that creates the application, so a program started from another folder
will not find its templates; always pass an absolute path, as in the first example. An array is searched in order, which
lets a theme override a default template. view engine sets the extension used when the name has none, so with
app.set('view engine', 'pug') the call res.render('index') loads index.pug. The engine package is loaded on first
use with require(), which is why the engine must be installed and (for ESM-only engines) why you may need app.engine(),
described in app.engine(), app.render() and app.path(). view cache makes Express remember the result of the file lookup and pass cache: true to
engines that cache compiled templates; it is on in production and should stay off while developing so edits appear on
reload. The engines themselves, layouts and escaping are covered in
Template Engines and Views.
x-powered-by
By default every response carries X-Powered-By: Express, which tells scanners which framework (and so which
vulnerabilities) to try. Turn it off with app.disable('x-powered-by'), or use Helmet, which removes the header as part of
its defaults. It is a hygiene measure, not a defence: the framework is usually identifiable from other signals. See
Security Best Practices.
app.locals
app.locals is a plain object whose properties become variables in every template rendered by the application. Unlike
res.locals, which lives for a single request, app.locals lasts for the application’s lifetime, so it is the right
place for values that never change per request: the site title, an asset version, a formatting helper. It also carries
one property set by Express, app.locals.settings, which is the same object as app.settings, so templates can read
settings such as settings.env.
When res.render() or app.render() builds the template’s variables, the three sources are merged in this order, with
later ones winning: app.locals, then res.locals, then the object passed to the render call. A value passed to the
call always overrides the other two, and a res.locals value set by middleware overrides a global default. Do not put
per-request data on app.locals: the object is shared between concurrent requests, so one user’s data would leak into
another user’s page. Note also that app.locals is a template concept; for configuration read in handlers use settings
or a configuration module.
The following program defines a template engine with app.engine(), sets locals at both levels, renders through a route
and through app.render(), and shows the precedence.
// render-demo.mjs
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import express from 'express';
await mkdir(join(import.meta.dirname, 'views'), { recursive: true });
await writeFile(join(import.meta.dirname, 'views', 'hello.bks'), '<h1>{{title}}</h1><p>{{who}}</p>');
const escapeHtml = (text) => String(text).replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
const app = express();
// An engine is a function (filePath, options, callback). `options` holds every merged local
// plus Express's own `settings` and `cache` entries.
app.engine('bks', (filePath, options, callback) => {
readFile(filePath, 'utf8').then(
(template) => {
const html = template.replace(/\{\{(\w+)\}\}/g, (_, key) => escapeHtml(options[key] ?? ''));
callback(null, html);
},
callback,
);
});
app.set('views', join(import.meta.dirname, 'views'));
app.set('view engine', 'bks');
app.locals.title = 'Bookshelf';
app.locals.who = 'from app.locals';
app.get('/hello', (req, res) => {
res.locals.who = 'from res.locals'; // overrides app.locals for this request
res.render('hello', { title: 'Render call' }); // overrides both
});
const server = app.listen(0, async () => {
const { port } = server.address();
console.log(await (await fetch(`http://localhost:${port}/hello`)).text());
app.render('hello', {}, (error, html) => {
console.log(error, html);
server.close();
});
});
<h1>Render call</h1><p>from res.locals</p>
null <h1>Bookshelf</h1><p>from app.locals</p>
app.engine(), app.render() and app.path()
app.engine(ext, callback) registers a template engine for a file extension. The callback has the signature
(filePath, options, callback), as in the example above, and calls callback(error, html) when done. Engines that export
a function named __express (Pug, EJS) are picked up automatically through view engine; use app.engine() for the
others, for ESM-only engines and to map an extension to an engine, such as app.engine('html', engine.renderFile).
Writing an engine is described in the official page for developing template engines, and the markdown example of
the expressjs/express repository is a complete engine in a few lines.
app.render(view, [locals], callback) does what res.render() does but returns the HTML to a callback instead of
sending it. It is the tool for rendering an e-mail body, a static page at build time or a fragment for a test.
res.render() calls it and passes the result to res.send(). In Express 5 the callback is always invoked
asynchronously, even for engines that render synchronously; Express 4 let a synchronous engine call back before
render() returned, which could expose ordering bugs. A render error goes to the callback, or to next(err) when
res.render() is called without one.
app.path() returns the canonical path of the application: an empty string for the top-level application, and the mount
path for a sub-application. If the mount path was an array, the returned string is the array joined with commas, as shown
in the example of the next section, so do not rely on it in that case.
Source for this section: Express: Developing template engines
Sub-applications, app.mountpath and the mount event
An application can be mounted inside another one with app.use(path, subApp). This is different from mounting an
express.Router(): a sub-application is a complete application with its own settings, app.locals, engine registry and
error handlers, which makes it the right tool for a self-contained piece such as an administration area with its own view
engine. A router is lighter and shares the parent’s settings, so prefer a router unless you need that isolation (see
Routers and Modular Apps).
| Member | Behaviour |
|---|---|
|
The path pattern, or array of patterns, on which the sub-application was mounted. For the top-level application it is
the string |
|
The parent application once mounted; |
|
Emitted on the sub-application, with the parent application as the only argument, when the sub-application is mounted
by |
|
The canonical mount path, as described above. |
Settings inheritance |
A sub-application inherits settings that have no default (for example a custom key). It does not inherit a setting that
has a default ( |
// sub-app.mjs
import express from 'express';
const app = express();
const admin = express();
admin.set('view engine', 'html'); // has a default of "none": set it here
admin.disable('x-powered-by'); // a default exists: not inherited, set it here
admin.on('mount', (parent) => {
console.log('admin mounted on', admin.mountpath, 'parent is the main app:', parent === app);
// Settings without a default are inherited, so a value set on the parent is visible here.
console.log('page size limit =', admin.get('page size limit'));
});
admin.get('/', (req, res) => {
res.json({ mountpath: req.app.mountpath, baseUrl: req.baseUrl, path: req.path });
});
app.set('page size limit', 50);
app.use(['/admin', '/manage'], admin); // an array of mount paths is allowed
console.log('app.mountpath =', app.mountpath, ' admin.path() =', JSON.stringify(admin.path()));
app.listen(3000);
admin mounted on [ '/admin', '/manage' ] parent is the main app: true
page size limit = 50
app.mountpath = / admin.path() = "/admin,/manage"
Inside a handler of the sub-application, req.app is the sub-application and req.baseUrl is the matched mount path, so
a handler should build links from req.baseUrl rather than hard-coding /admin. req.originalUrl always keeps the full
original URL. When the mount path is an array, app.mountpath is the array; code that needs a single prefix should take
req.baseUrl.
Starting a server
app.listen()
app.listen() creates an http.Server with the application as its request listener and calls server.listen() with the
same arguments, so every form of server.listen() works: a port (app.listen(3000)), a port with a host and a backlog, a
Unix socket path and a listening callback. It returns the http.Server. Keep that reference: you need it to close the
server during a graceful shutdown (see
Health Checks and Graceful Shutdown) and to read the actual
port when you ask for port 0, which lets the operating system choose a free port and is what tests should use.
Express 5 changed the callback. When the last argument is a function, Express registers it for the listening event and
also for the server’s error event, wrapped so that it runs once. On success it is called with no arguments; if the
server fails to start (for example EADDRINUSE because the port is taken, or EACCES for a privileged port), it is called
with the Error. Express 4 never passed an error, and without a listener for error the process crashed with an
unhandled event.
// src/server.js
import { createApp } from './app.js';
const app = createApp();
const port = Number(process.env.PORT ?? 3000);
const server = app.listen(port, (error) => {
// Express 5: runs once, with an error if the server could not start.
if (error) {
console.error(`cannot listen on ${port}: ${error.code ?? error.message}`);
process.exit(1);
}
console.log(`bookshelf-api listening on http://localhost:${server.address().port}`);
});
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
});
The program starts the application built in the first example. Keeping createApp() free of listen() calls, and putting
the listening in a separate entry file, is what allows tests to import the application and call it through supertest
without opening a port (see Testing). A callback attached to the listen call of a
server that fails after it was listening (an error emitted later) is not called again, because it was registered once; for
long-lived errors attach server.on('error', handler) too.
http.createServer(app), HTTPS and HTTP/2
Because the application is a request listener, app.listen() can always be replaced by creating the server yourself. You
need to do that to serve HTTPS, to share the server with a WebSocket library (see
Real Time: WebSockets and SSE), to listen on several ports with one
application, and to set the server timeouts.
// src/server-https.js
import { readFileSync } from 'node:fs';
import http from 'node:http';
import https from 'node:https';
import { createApp } from './app.js';
const app = createApp();
// Placeholders: the paths come from the environment, the key and certificate are never committed.
const tlsOptions = {
key: readFileSync(process.env.TLS_KEY_FILE),
cert: readFileSync(process.env.TLS_CERT_FILE),
};
const httpServer = http.createServer(app).listen(8080);
const httpsServer = https.createServer(tlsOptions, app).listen(8443);
// Node.js timeouts worth setting explicitly behind a load balancer.
httpsServer.keepAliveTimeout = 65_000; // longer than the balancer's idle timeout
httpsServer.headersTimeout = 66_000; // must exceed keepAliveTimeout
export { httpServer, httpsServer };
Source: Node.js: HTTPS and Express API: Application (app.listen)
Most production deployments terminate TLS at a reverse proxy or load balancer and run Express over plain HTTP, with
trust proxy set; that is simpler to operate and renews certificates without restarting the process.
HTTP/2 deserves a caution. Node.js has a compatibility layer in node:http2, but Express was written against the
node:http HTTP/1 request and response classes and the official documentation does not describe HTTP/2 as a supported
setup, so passing an Express application to http2.createSecureServer() is not a documented configuration. The usual
and supported way to offer HTTP/2 to browsers is to terminate it at a proxy or CDN that speaks HTTP/1.1 to Express.
Third-party adapters exist; evaluate them for Express 5 compatibility and maintenance before relying on them (see
Where the books are outdated for the equivalent warning about unmaintained middleware).
app.router
app.router is the application’s base router: the object that holds the stack built by app.use(), app.METHOD() and
app.all(). Express 3 required app.use(app.router) to control the position of the routes in the stack; Express 4 removed
the property and made access throw an error with a migration message; Express 5 brings it back as a plain reference to the
router, so no app.use(app.router) call is needed or wanted. In other words, app.use(fn) and app.router.use(fn) do
the same thing, and the router is created on first access, which is the reason the routing settings must come first (see
case sensitive routing and strict routing).
You rarely need the property. Its practical uses are attaching a handler to the base router explicitly and, for
debugging, listing the registered routes. The stack array that the example walks is an implementation detail of the
router package, not a documented API, so use it for diagnostics only and not as a contract.
// list-routes.mjs
import express from 'express';
const app = express();
app.get('/books', (req, res) => res.json([]));
app.post('/books', express.json(), (req, res) => res.status(201).json(req.body));
app.use('/authors', express.Router().get('/', (req, res) => res.json([])));
for (const layer of app.router.stack) {
if (layer.route) {
console.log(Object.keys(layer.route.methods).join(',').toUpperCase(), layer.route.path);
} else {
console.log('USE', layer.name);
}
}
GET /books
POST /books
USE router
Express 4 compared with Express 5
| Area | Express 4 | Express 5 |
|---|---|---|
|
|
|
|
Called on |
Called once, with the |
|
Removed; access threw an error. |
Back, as a reference to the base router. |
|
Deprecated alias of |
Removed. |
|
Deprecated. |
Removed. See Route Paths and Parameters. |
|
Synchronous for synchronous engines. |
Always asynchronous. |
Path strings in |
Parsed by path-to-regexp 0.1. |
Parsed by path-to-regexp 8, so |
|
Not available. |
Added in 5.3 for the HTTP |
|
Removed (since Express 4). |
Not available. |
What changed recently: Express 5.0.0 (2024-09) introduced the changes in the table; 5.1.0 (2025-03-31) became the latest
tag on npm, so npm install express installs Express 5 and old projects that depended on the unpinned express name began
to receive it on a fresh install; 5.3.0 (2026-10-09) added app.query(). The step-by-step upgrade, with the codemods, is
in Migrating and What Changed.
Where the books are outdated
The four consulted books build applications on Express 4.0 to 4.18, and their application-setup code follows the
express-generator scaffold of the time. The concepts hold; the details below have changed.
| Older practice | Where it appears | Current behaviour |
|---|---|---|
Creating the server in a |
Generator-based chapters: Sharma, chapter 2; Monteiro 2014, chapters 3 and 4; Monteiro 2016, chapters 1 and 2 |
Still valid. |
Setting the view engine to |
The same generator-based chapters |
Jade was renamed Pug in 2016 and the old package is deprecated. Use |
Building the views and static paths with |
The same generator-based chapters |
In ESM there is no |
Reading nested query-string parameters from |
Books written for Express 4 |
Express 4 parsed them with |
Calling |
Bugl |
In Express 5 the callback receives the error, which is the place to log and exit instead of crashing on an unhandled
|
Deploying behind a PaaS router with |
Sharma, chapter 10; Monteiro 2014, chapter 8 |
Cookies marked |
|
All four books |
Disable |
|
Older tutorials in general |
|