create-express-new
v1.4.0
Published
Express 5 application generator: ESM, TypeScript and CommonJS app templates. A modern successor to express-generator.
Maintainers
Readme
create-express-new
An application generator for Express 5, creating web apps or JSON APIs as ES modules, TypeScript or CommonJS that run on current Node.js with no build step.
create-express-new is a fork of express-generator,
the original Express application generator, reimagined for today's Express apps. It keeps the
familiar express command and project layout, and updates everything it generates for Express 5
and modern Node.js. It is not affiliated with or endorsed by the Express project.
Quick Start
Run the generator with no arguments to be guided through the options (requires Node.js 22.9 or newer):
$ pnpm create express-new@latestnpm create express-new@latest starts the same wizard. It asks for:
- the project directory (
.for the current directory) - a web app, a JSON API or a web app with a JSON API, and the view engine
- the language: JavaScript, TypeScript or CommonJS
- optional middleware, and CSRF protection with sessions
- the request logger
- extras: a Dockerfile, ESLint and Sass
- a
.gitignore - the package manager the app requires: pnpm, or npm
Esc (or ←) goes back a question, keeping your answers. At the end the wizard shows the equivalent command, to repeat the setup or use in scripts, and offers to install the dependencies.
In a directory that already has files:
- Files the app would replace are listed, and the wizard asks whether to overwrite them, keep your
config files (see
--keep-configbelow) or cancel. - Files with the same contents are left alone.
- An existing
.gitignoreis never replaced; the lines it is missing are added at the end. - A file where the app needs a folder, or a folder where it needs a file, stops anything being written.
- Files from an earlier app with other options, such as an
app.jsnext to a newapp.ts, are left in place and listed in a warning.
Or pass the directory and options directly:
$ pnpm create express-new@latest my-app --ts --helmet
$ cd my-app
$ pnpm installWith npm create, put the options after --: npm create express-new@latest my-app -- --ts --helmet.
npx create-express-new my-app --ts --helmet needs no --. Either way the app requires pnpm unless
you add --pm=npm. Add --install to install the dependencies too, with the app's package manager.
Start it at http://localhost:3000/, and run its tests, written with the built-in
node:test runner:
$ pnpm dev # restarts on change
$ pnpm start # without restarting
$ pnpm testAn app generated with --pm=npm uses npm install, npm run dev, npm start and npm test.
You can also install the generator globally, which provides the express command:
$ npm install -g create-express-new
$ express --view=ejs --helmet my-appThe original express-generator provides an express command too, so install only one of them globally.
What You Get
my-app
├── .env.example # example environment variables; copy to .env
├── .gitignore # ignores node_modules, .env files, logs and coverage
├── app.js # the Express app: middleware, views, routes, error handling
├── bin/www.js # starts the server, with graceful shutdown on SIGINT/SIGTERM
├── package.json # "type": "module", start/dev/test scripts
├── public/ # static files, including stylesheets/style.css
├── routes/ # index.js and users.js routers
├── test/app.test.js # node:test tests for the app
└── views/ # error, index, layout and users templates (pug by default)Every app has a GET /health endpoint that responds with { "status": "ok" }, for load balancers,
container health checks and uptime monitors. It is defined before the middleware, so it stays fast and
out of the request logs.
Server errors (5xx) are logged with console.error; client errors (4xx), such as a 404, are not.
Errors render with the error view, or as plain text with --no-view. Client errors show their
message. Server errors only say Internal Server Error outside development, so internal details are
never sent; development shows every error's message and stack.
JSON APIs
--api generates a JSON API instead of a web app: no views, static files or form parsing, and
routes that respond with JSON.
my-api
├── app.js # JSON body parsing, routes, JSON 404 and error handling
├── bin/www.js
├── package.json
├── routes/ # index.js and users.js, responding with JSON
└── test/app.test.jsErrors are always JSON, such as { "error": "Not Found" }, and follow the same rules as web apps:
client errors include their message, server errors only say Internal Server Error in production,
and development adds the stack trace. Express runs in development mode unless NODE_ENV is set, so
set NODE_ENV=production when you deploy.
--api works with --cjs, --ts and the optional middleware, except --session.
Web apps with a JSON API
--api with a view engine, such as --api --view=pug, generates a web app that has a JSON API too:
everything a web app has, and a routes/api folder of routers mounted at /api, apart from the
routers of the pages.
my-app
├── app.js # the web app, with JSON errors under /api
├── routes/
│ ├── api/
│ │ ├── index.js # JSON routes: GET /api, and the routers of the API's resources
│ │ └── users.js # GET /api/users
│ ├── index.js # the home page
│ └── users.js # the users pages
├── views/
└── ...To add a resource to the API, add its router to routes/api and mount it in routes/api/index.js.
Every error of a request under /api is JSON, in the same shape as a JSON API's: the 404 of an
unknown route, an error a route throws, and the errors of the middleware before the routes, such as a
request body that is not valid JSON. Every other error renders the error view.
With --cors and --csrf, you choose which routes they apply to:
--cors-scope=api(default) lets other origins call the API only, not read the pages;--cors-scope=alllets them call the whole app.--csrf-scope=all(default) requires the CSRF token under/apitoo, for an API your own pages call;--csrf-scope=pagesleaves the API out, for an API other clients call without the session cookie.
The wizard asks both.
The view engine has to be given, as --api alone is a JSON API without views: use --view=pug for
the default one. --api does not add an API to a --no-view app.
Module formats
- ES modules (default):
import/export,import.meta.dirnameandnode:built-ins. - CommonJS (
--cjs): the same app withrequire()/module.exports. - TypeScript (
--ts):.tsfiles that Node.js 22.18+ runs directly by stripping types, so there is no build step. Includes a stricttsconfig.json, the@typespackages the app needs and atypecheckscript that runstsc.--tscannot be combined with--cjs.
Configuration
The start and dev scripts load environment variables from a .env file when one exists, using
Node.js's built-in --env-file-if-exists,
so no dotenv package is needed. Start from the generated example:
$ cp .env.example .envVariables already set in the environment take precedence over .env, so your hosting platform's
settings always win. The dev script restarts when .env changes. The generated .gitignore keeps
.env, and every other .env.* file except .env.example, out of version control.
Optional middleware
By default, generated apps include only request logging, body parsing and static files (no static
files with --api). Add more with:
--helmet: helmet security headers--compression: gzip/brotli response compression--cookies: cookie-parser, for readingreq.cookies--cors: cors, allowing requests from other origins--rate-limit: express-rate-limit, limiting each client to 100 requests every 15 minutes--session(not for JSON APIs without views): express-session, forreq.session--csrf(with--sessionand a view engine): csrf-sync CSRF protection for forms--uploads: multer and aPOST /uploadsroute that takes one file of up to 5 MB
Optional middleware covers each one's settings and environment variables, sending the CSRF token from forms and JavaScript, running behind a proxy, and how uploaded files are stored and checked.
Request logging
Requests are logged with morgan by default, one readable line
per request. --logger=pino uses pino-http instead, which logs
JSON for log collectors and adds req.log for your own logs. The dev script pipes them through
pino-pretty to keep them readable. Either way, requests are
not logged while the tests run.
Package manager
Every app requires one package manager, pnpm by default or npm with --pm=npm, so other package
managers refuse to install the app or run its scripts. It also has settings that protect installs
from compromised packages: new releases wait 3 days before they are installed, and the install
scripts of dependencies are not run. An npm app needs npm 12 or newer, which does not come with
Node.js 22 or 24.
Package manager compares the pnpm and npm settings, and covers allowing a dependency's install script, installing a release sooner and changing package manager later.
Docker
--docker adds a Dockerfile and .dockerignore for a production image. Install dependencies
first, as the image installs from the lockfile:
$ pnpm install
$ docker build -t my-app .
$ docker run -p 3000:3000 my-appThe image:
- is based on
node:22-slim, the latest release of the minimum supported Node.js version - installs only production dependencies, in a build stage of their own, so the package manager is not in the final image
- with
--sass, compiles the stylesheets in another build stage, so Sass is not in the final image - runs the app as the unprivileged
nodeuser withNODE_ENV=production - checks
/healthwith aHEALTHCHECK - runs
nodedirectly, so it receivesSIGTERMand shuts down gracefully - does not copy
.envfiles; set environment variables with your container platform instead. With--session, the app needsSESSION_SECRETto start, such asdocker run -e SESSION_SECRET=... -p 3000:3000 my-app
Linting
--lint adds ESLint with its recommended rules and a lint script. With
--ts it also adds typescript-eslint; as typescript-eslint does not
support TypeScript 7 yet, these apps use TypeScript 6.
Sass
--sass adds Sass to a web app: a public/stylesheets/style.scss in place
of style.css, compiled to the CSS the app serves.
$ pnpm start # compiles the stylesheets, then starts the app
$ pnpm dev # compiles them once, then restarts the app on change
$ pnpm css:watch # recompiles them on change, in a second terminalSass is a development dependency, so pnpm start fails where only production dependencies are
installed: compile the stylesheets first with pnpm css, as the --docker image does.
Using Sass covers this, the files it ignores and adding Sass to an existing app.
Command Line Options
-v, --view <engine> add view <engine> support (ejs|hbs|pug|twig) (defaults to pug)
--no-view use static html instead of view engine
--api generate a JSON API, without views or static files
(with --view, a web app with a JSON API at /api)
--cjs generate CommonJS modules instead of ES modules
--ts generate TypeScript, run directly by Node.js
--helmet add helmet middleware for security headers
--compression add compression middleware for gzip/brotli responses
--cookies add cookie-parser middleware
--cors add cors middleware for cross-origin requests
--cors-scope <scope>
with --api and --view, the routes other origins can
call (api|all) (defaults to api)
--rate-limit add express-rate-limit to limit requests per client
--session add express-session for sessions (not with --api alone)
--csrf add CSRF protection for forms (needs --session)
--csrf-scope <scope>
with --api and --view, the routes that need a CSRF
token (all|pages) (defaults to all)
--uploads add multer and an upload route at /uploads
--logger <name> request logger (morgan|pino) (defaults to morgan)
--sass add Sass, compiling .scss stylesheets (not with --api alone)
--docker add a Dockerfile for a production image
--lint add ESLint and a lint script
--pm <name> package manager the app requires (pnpm|npm) (defaults to pnpm)
--install install the dependencies after creating the app
--no-git skip the .gitignore
-f, --force force on non-empty directory
--keep-config keep existing config files (.env.example, Dockerfile,
.dockerignore, tsconfig.json, eslint.config.*,
pnpm-workspace.yaml, .npmrc)
--version output the version number
-h, --help output usage informationChanges From express-generator
This fork started from express-generator 4.16.1. It generates Express 5 apps as ES modules, with
pug in place of jade, for Node.js 22.9 or newer. --view=<engine> replaces the --ejs, --hbs and
--pug aliases, and apps use plain CSS unless you add Sass with --sass; the other CSS preprocessors
are gone.
- Changes from express-generator: everything added, changed and removed, and a table for migrating commands
- Using Sass: what
--sassadds, and adding Sass to an existing app
Contributing
$ pnpm install
$ pnpm test # generates, installs and runs apps for each option
$ pnpm lintThis repository requires pnpm 11 or newer, as generated apps do by default, so install pnpm first; npm refuses to install it or run its scripts. The tests that install an npm app are skipped unless your npm is version 12 or newer.
See Maintaining for keeping the dependency versions of generated apps current, and for releasing.
License
MIT. Originally created by TJ Holowaychuk and the Express contributors.
