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"
}
}
| Task | Code | Lesson |
|---|---|---|
Load .env (Node 20.6+) | node --env-file=.env server.js | Introduction to Express.js |
Load .env with dotenv | import "dotenv/config"; | Introduction to Express.js |
| Read a setting | process.env.PORT ?? 3000 | Introduction to Express.js |
| Generate a project | npx express-generator --view=ejs app | Application Structure |
| Run TypeScript | npx tsx src/server.ts | Express 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");
});
| Setting | What it does |
|---|---|
env | From NODE_ENV. production hides error details |
x-powered-by | Adds X-Powered-By: Express. Turn it off |
json spaces | Pretty JSON |
trust proxy | Read 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, views | Templates for res.render |
Requests and Responses
req | Gives you |
|---|---|
req.method, req.path, req.originalUrl, req.baseUrl | Method and URL parts |
req.params, req.query, req.body | Path 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.hostname | Connection details |
req.is("json"), req.accepts("json") | Body type, preferred answer type |
req.cookies, req.signedCookies, req.session | With cookie-parser and express-session |
res | Does |
|---|---|
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.locals | Data 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 4 | Express 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.type | Status |
|---|---|
entity.parse.failed | 400 |
entity.too.large | 413 |
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 tag | Meaning |
|---|---|
<%= 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
| Action | Request | Success |
|---|---|---|
| List | GET /api/v1/menu?veg=true&sort=-price&page=2&limit=10 | 200 { data, meta } |
| Create | POST /api/v1/menu | 201 + Location |
| Read | GET /api/v1/menu/3 | 200, or 404 |
| Replace | PUT /api/v1/menu/3 | 200 |
| Change part | PATCH /api/v1/menu/3 | 200 |
| Delete | DELETE /api/v1/menu/3 | 204 |
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
| Redirect | Use |
|---|---|
| 301 / 308 | Moved for good (308 keeps the method) |
| 302 / 307 | Temporary (307 keeps the method) |
| 303 | After 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
- Settings:
trust proxy,x-powered-by, view engine. - Security:
helmet,cors, rate limits. - Logging.
- Body parsers, cookies, sessions.
- Static files.
- Authentication.
- Routers.
- 404 handler.
- 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()andres.sendfile()(lowercase f). req.queryis read-only, and the query parser is"simple"by default.express.urlencodedusesextended: falseby default, andreq.bodyisundefinedwhen not parsed.- Remove
try/catchwrappers that only forwarded async errors. Express 5 does it. - Check the
app.listencallback for anerrorargument.
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.