Express.js Cheat Sheet

By Syncfusion®, Inc. · Last updated

A one-page reference for Express 5: project setup, application settings, request and response methods, routing and Express 5 path syntax, routers, middleware, views, static files, sessions, files, REST status codes, errors, redirects, proxies, logging, security, registration order and an Express 4 to 5 migration checklist.

Express.js Cheat Sheet

Every important Express 5 API from this course, on one page.

  • Each section links back to the lesson that teaches it.
  • Code uses ES modules (import) and Express 5.
  • Copy the starter project at the bottom to begin a new app.

Project Setup

mkdir canteen-api && cd canteen-api
npm init -y
npm install express
npm install --save-dev supertest
node --version   # Express 5 needs Node.js 18 or later
{
  "type": "module",
  "scripts": {
    "dev": "node --watch --env-file=.env src/server.js",
    "start": "node src/server.js",
    "test": "node --test"
  }
}
TaskCodeLesson
Load .env (Node 20.6+)node --env-file=.env server.jsIntroduction to Express.js
Load .env with dotenvimport "dotenv/config";Introduction to Express.js
Read a settingprocess.env.PORT ?? 3000Introduction to Express.js
Generate a projectnpx express-generator --view=ejs appApplication Structure
Run TypeScriptnpx tsx src/server.tsExpress with TypeScript

Application

import express from "express";

const app = express();
app.set("json spaces", 2);        // a setting
app.get("json spaces");           // read it (one argument)
app.disable("x-powered-by");
app.locals.school = "Green Valley";

app.listen(3000, (error) => {
  if (error) throw error;         // Express 5: port errors arrive here
  console.log("Ready on http://localhost:3000");
});
SettingWhat it does
envFrom NODE_ENV. production hides error details
x-powered-byAdds X-Powered-By: Express. Turn it off
json spacesPretty JSON
trust proxyRead X-Forwarded-* from trusted proxies
case sensitive routing, strict routing/Menu vs /menu, /menu vs /menu/
query parser"simple" by default in Express 5
view engine, viewsTemplates for res.render

Requests and Responses

reqGives you
req.method, req.path, req.originalUrl, req.baseUrlMethod and URL parts
req.params, req.query, req.bodyPath values, query values, parsed body (all untrusted)
req.get("X-Name")One header, any letter case
req.ip, req.ips, req.protocol, req.secure, req.hostnameConnection details
req.is("json"), req.accepts("json")Body type, preferred answer type
req.cookies, req.signedCookies, req.sessionWith cookie-parser and express-session
resDoes
res.send(x), res.json(x), res.end()Send text, HTML or JSON
res.status(201), res.sendStatus(204)Set the status (and send its text)
res.set(name, value), res.append(...), res.type("json")Headers
res.localsData for this request only
res.redirect(301, "/menu"), res.location(url)Redirects and Location
res.cookie(...), res.clearCookie(...)Cookies
res.sendFile(name, { root }), res.download(path, name)Files
res.render("view", data), res.format({...})Templates, negotiation

Rule: one response per request. Use return res.send(...) to stop early.

Routing

app.get("/menu", handler);
app.post("/menu", handler);
app.route("/orders").get(list).post(create);
app.all("/closed", (req, res) => res.sendStatus(503));

app.get("/menu/:category/:id", (req, res) => res.json(req.params)); // strings
app.get("/orders{/:id}", handler);      // optional part
app.get("/files/*splat", handler);      // req.params.splat is an array
app.get("/menu/:id", [checkId, load], show);   // handler chain
app.get("/menu/:id", (req, res, next) => next("route")); // skip to the next route
Express 4Express 5
"*""/*splat"
"/:id?""{/:id}"
res.redirect(url, 301)res.redirect(301, url)
res.redirect("back")res.redirect(req.get("Referrer") || "/")
req.param("id")req.params.id, req.query.id or req.body.id
Async errors need next(err)Rejected promises reach error middleware

Router

const router = express.Router({ mergeParams: true });
router.use(onlyForThisRouter);
router.param("id", (req, res, next, id) => { /* load req.item or 404 */ next(); });
router.get("/", list);
router.get("/:id", show);
export default router;

app.use("/api/v1/menu", router);

Middleware and Body Parsers

function logger(req, res, next) {
  res.on("finish", () => console.log(req.method, req.originalUrl, res.statusCode));
  next();                                   // or send a response, or next(err)
}

const requireRole = (role) => (req, res, next) =>
  req.user?.role === role ? next() : res.status(403).json({ error: "Forbidden" });

app.use(express.json({ limit: "10kb" }));    // application/json
app.use(express.urlencoded());               // HTML forms (extended: false in Express 5)
app.post("/note", express.text(), handler);  // text/plain
app.post("/webhook", express.raw({ type: "application/json" }), handler); // exact bytes
Parser error err.typeStatus
entity.parse.failed400
entity.too.large413

Views and Static Files

app.set("view engine", "ejs");               // npm install ejs
app.get("/menu", (req, res) => res.render("menu", { items }));

app.use(express.static(path.join(import.meta.dirname, "public"), { maxAge: "1d" }));
app.use("/assets", express.static("public")); // virtual prefix
app.get("/*splat", (req, res) => res.sendFile(path.join(dist, "index.html"))); // SPA fallback, last
EJS tagMeaning
<%= value %>Print, escaped (safe)
<%- html %>Print raw (trusted HTML only)
<% code %>Run JavaScript
<%- include("partials/header") %>Insert a partial

Input, Cookies and Sessions

// Validation middleware: 400 unreadable, 422 broken rules
const validate = (rules) => (req, res, next) => { /* collect errors */ next(); };

app.use(cookieParser(process.env.COOKIE_SECRET));
res.cookie("theme", "dark", { httpOnly: true, secure: true, sameSite: "lax", maxAge: 86400000 });

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { httpOnly: true, secure: true, sameSite: "lax" },
}));
req.session.regenerate(cb);   // after login
req.session.destroy(cb);      // on logout

Files

const upload = multer({ limits: { fileSize: 2_000_000 }, fileFilter });
app.post("/photo", upload.single("photo"), (req, res) => res.json(req.file));
app.post("/pages", upload.array("pages", 5), handler);   // req.files
res.download(path.join(dir, "menu.pdf"), "canteen-menu.pdf");

Multer error codes: LIMIT_FILE_SIZE (413), LIMIT_FILE_COUNT (400), LIMIT_UNEXPECTED_FILE (400).

Database

import { DatabaseSync } from "node:sqlite";      // Node.js 22.5+
const db = new DatabaseSync("canteen.db");
db.exec("CREATE TABLE IF NOT EXISTS menu (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)");
const find = db.prepare("SELECT * FROM menu WHERE id = ?");   // ? = safe parameter
find.get(3);                                                    // one row or undefined
db.prepare("SELECT * FROM menu").all();                         // every row
const { lastInsertRowid, changes } = db.prepare("INSERT INTO menu (name, price) VALUES (?, ?)").run("Idli", 30);
db.exec("BEGIN"); /* ...several writes... */ db.exec("COMMIT"); // or ROLLBACK on error

Never build SQL by joining user text. Lesson: Working with a Database.

REST APIs

ActionRequestSuccess
ListGET /api/v1/menu?veg=true&sort=-price&page=2&limit=10200 { data, meta }
CreatePOST /api/v1/menu201 + Location
ReadGET /api/v1/menu/3200, or 404
ReplacePUT /api/v1/menu/3200
Change partPATCH /api/v1/menu/3200
DeleteDELETE /api/v1/menu/3204

API docs: serve an OpenAPI file at /api/openapi.json, and a try-it page with swagger-ui-express.

Errors: 400 bad request, 401 not logged in, 403 not allowed, 404 not found, 409 conflict, 422 invalid, 429 too many, 500 server bug.

Token Authentication

import jwt from "jsonwebtoken";
const token = jwt.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: "15m" });

function requireAuth(req, res, next) {
  const [scheme, token] = (req.get("Authorization") ?? "").split(" ");
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ["HS256"] });
    next();
  } catch (err) {
    res.status(401).json({ error: err.name === "TokenExpiredError" ? "Token expired" : "Invalid token" });
  }
}
const requireRole = (role) => (req, res, next) => req.user.role === role ? next() : res.status(403).json({ error: "Not allowed" });

401 = who are you? 403 = you may not. Check ownership with req.user.sub. Lesson: Token Authentication and Authorization.

Errors, Redirects and Proxies

app.use((req, res) => res.status(404).json({ error: "Not found" }));   // after routes
app.use((err, req, res, next) => {                                      // four inputs, last
  if (res.headersSent) return next(err);
  res.status(err.status ?? 500).json({ error: err.status < 500 ? err.message : "Something went wrong" });
});

res.format({ json: () => res.json(data), html: () => res.render("page", data) });
res.redirect(303, "/orders/thanks");                // after a form POST
app.set("trust proxy", 1);                          // one proxy in front
RedirectUse
301 / 308Moved for good (308 keeps the method)
302 / 307Temporary (307 keeps the method)
303After a form POST

Calling APIs, Jobs and Live Updates

const r = await fetch("https://api.weather.lab/today?city=Chennai", { signal: AbortSignal.timeout(2000) });
if (!r.ok) return res.status(502).json({ error: "Weather service failed" });   // fetch does not throw on 4xx/5xx
const data = await r.json();

res.status(202).json({ jobId });                     // slow work: answer now, finish later
sendEmail(order).catch((err) => console.error(err)); // fire-and-forget always needs .catch

res.set("Content-Type", "text/event-stream");        // Server-Sent Events
res.write("data: " + JSON.stringify(update) + "\n\n");
req.on("close", stop);

Retry only 503, 429 and network errors, with backoff. Send an Idempotency-Key with payments. Lesson: Calling APIs, Background Jobs and Real-Time.

Logging and Security

app.use(morgan("combined", { skip: (req) => req.path === "/health" }));
app.use(helmet());
app.use("/api", cors({ origin: ["https://canteen.school.in"], credentials: true }));
app.use("/login", rateLimit({ windowMs: 15 * 60 * 1000, limit: 5 }));
DEBUG=express:* node server.js     # Express's own debug output
npm audit                          # known problems in packages

Deploying

const port = Number(process.env.PORT ?? 3000);                 // the host picks the port
const server = app.listen(port);
app.get("/healthz", (req, res) => res.json({ status: "ok" }));  // liveness
process.on("SIGTERM", () => server.close(() => process.exit(0))); // finish requests, then exit
NODE_ENV=production node src/server.js
pm2 start src/server.js -i max
docker build -t canteen-api . && docker run -p 3000:3000 -e JWT_SECRET=... canteen-api

Lesson: Deploying an Express App.

TypeScript

npm install -D typescript tsx @types/express @types/node
npx tsx watch src/server.ts      # run while developing
npx tsc --noEmit                 # type-check
npx tsc && node dist/server.js   # build and start in production
import type { Request, Response } from "express";
app.get("/api/menu/:id", (req: Request<{ id: string }>, res: Response<MenuItem | { error: string }>) => { /* ... */ });

declare global { namespace Express { interface Request { user?: { id: string; role: string } } } }

Lesson: Express with TypeScript.

Registration Order

  1. Settings: trust proxy, x-powered-by, view engine.
  2. Security: helmet, cors, rate limits.
  3. Logging.
  4. Body parsers, cookies, sessions.
  5. Static files.
  6. Authentication.
  7. Routers.
  8. 404 handler.
  9. Error handler.

Express 4 to 5 Migration Checklist

  • Node.js 18 or later.
  • Rename wildcards: "*" to "/*splat", and optional ? params to {/:id}.
  • Remove regex characters ( ) [ ] + ! from string paths.
  • Put the status first in res.redirect(status, url). Replace "back".
  • Replace req.param(), app.del() and res.sendfile() (lowercase f).
  • req.query is read-only, and the query parser is "simple" by default.
  • express.urlencoded uses extended: false by default, and req.body is undefined when not parsed.
  • Remove try / catch wrappers that only forwarded async errors. Express 5 does it.
  • Check the app.listen callback for an error argument.

Starter Project

Press Download to get this starter as a zip, with a package.json ready for npm install.

What to Remember

  • This page is a reminder, not a replacement for the lessons. Follow the lesson names to learn each part.
  • When in doubt, check the official Express 5 docs at expressjs.com.

Next: You have finished the course. Build the canteen API from the capstone on your own computer, then deploy it.