Node.js and Express Cheat Sheet
Running Node, modules, files and paths, process and events, streams and the event loop, Express routes, middleware and errors, status codes, validation, SQLite, auth, security and testing.
Updated
Run and set up#
node app.js # run a file
node --watch app.js # restart on changes
node --env-file=.env app.js # load environment variables
node --test # run tests (node:test)
npm init -y && npm pkg set type=module
npm install express # dependency
npm install -D supertest # dev dependency
npm ci --omit=dev # exact, production-only installModules#
// ES modules ("type": "module")
import express from "express";
import { readFile } from "node:fs/promises";
import { greet } from "./utils.js"; // relative imports need the extension
export function add(a, b) { return a + b; }
export default class Cart {}
const here = import.meta.dirname; // __dirname in CommonJS
// CommonJS
const { greet } = require("./utils.cjs");
module.exports = { add };Files and paths#
import { appendFile, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
import path from "node:path";
const text = await readFile("notes.txt", "utf8");
await writeFile("out.json", JSON.stringify(data, null, 2));
await appendFile("app.log", "started\n");
await mkdir("reports/2026", { recursive: true });
const names = await readdir("reports");
await rm("tmp", { recursive: true, force: true });
try { await readFile("missing.json", "utf8"); }
catch (err) { if (err.code !== "ENOENT") throw err; }
path.join("data", "orders.json"); // OS separator
path.basename("a/b/report.pdf"); // "report.pdf"
path.extname("report.pdf"); // ".pdf"
path.join(import.meta.dirname, "public"); // relative to this fileProcess, URLs, events#
process.argv.slice(2); // command-line arguments
process.env.PORT ?? "3000"; // env vars are strings
process.exitCode = 1; // fail without cutting off output
const url = new URL("https://example.com/search?q=lamp&page=2");
url.searchParams.get("page"); // "2"
import { EventEmitter, once } from "node:events";
const bus = new EventEmitter();
bus.on("order", (o) => console.log(o.id));
bus.on("error", (err) => console.error(err)); // always handle 'error'
bus.emit("order", { id: 7 });Streams and the event loop#
import { createReadStream, createWriteStream } from "node:fs";
import { createInterface } from "node:readline";
import { pipeline } from "node:stream/promises";
import { createGzip } from "node:zlib";
for await (const line of createInterface({ input: createReadStream("big.log") })) { /* one line */ }
await pipeline(createReadStream("big.log"), createGzip(), createWriteStream("big.log.gz"));
const [user, orders] = await Promise.all([getUser(), getOrders()]); // run in parallelOrder: synchronous code → microtasks (promises, process.nextTick) → timers → I/O → setImmediate. Never block the event loop with long synchronous work.
Express essentials#
import express from "express";
const app = express();
app.use(express.json({ limit: "100kb" }));
app.get("/api/items", (req, res) => res.json(items)); // req.query.page
app.get("/api/items/:id", (req, res) => { /* req.params.id */ });
app.post("/api/items", (req, res) => res.status(201).json(created)); // req.body
app.delete("/api/items/:id", (req, res) => res.sendStatus(204));
const router = express.Router();
router.get("/", list);
app.use("/api/orders", router);
app.use(express.static(path.join(import.meta.dirname, "public")));
app.listen(Number(process.env.PORT ?? 3000));Express 5 paths: optional "/reports{/:year}", wildcard "/files/*path".
Middleware and errors#
function requireAuth(req, res, next) {
if (!req.get("authorization")) return res.status(401).json({ error: "Login required" });
next();
}
app.get("/api/me", requireAuth, handler);
app.use((req, res) => res.status(404).json({ error: "Not found" })); // after routes
app.use((err, req, res, next) => { // 4 params, last
const status = err.status ?? 500;
res.status(status).json({ error: status >= 500 ? "Internal error" : err.message });
});Express 5 forwards errors thrown in async handlers to the error middleware.
Status codes#
200 OK · 201 Created · 204 No Content · 301/302 Redirect · 400 Bad Request · 401 Unauthenticated · 403 Forbidden · 404 Not Found · 409 Conflict · 413 Payload Too Large · 429 Too Many Requests · 500 Server Error
Validation (Zod)#
import { z } from "zod";
const Product = z.object({
name: z.string().trim().min(2),
price: z.number().positive(),
page: z.coerce.number().int().min(1).default(1), // query strings → numbers
});
const result = Product.safeParse(req.body);
if (!result.success) return res.status(400).json({ errors: result.error.issues });SQLite (node:sqlite)#
import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync("app.db");
db.exec("CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, text TEXT NOT NULL)");
const { lastInsertRowid } = db.prepare("INSERT INTO notes (text) VALUES (?)").run(text);
db.prepare("SELECT * FROM notes WHERE id = ?").get(id); // one row or undefined
db.prepare("SELECT * FROM notes WHERE text LIKE :q").all({ q: "%milk%" });
db.exec("BEGIN"); /* ... */ db.exec("COMMIT"); // or ROLLBACK on errorAlways use placeholders (?, :name); never build SQL from user input.
Auth and security#
import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
import jwt from "jsonwebtoken";
import helmet from "helmet";
import cors from "cors";
const token = jwt.sign({ sub: String(user.id), role: user.role }, process.env.JWT_SECRET, { expiresIn: "1h" });
const claims = jwt.verify(token, process.env.JWT_SECRET); // throws if invalid or expired
app.use(helmet());
app.use(cors({ origin: ["https://shop.example.com"] }));Hash passwords with scrypt/bcrypt/Argon2 and a salt; rate-limit logins; keep secrets in env vars.
Testing#
import assert from "node:assert/strict";
import { test } from "node:test";
import request from "supertest";
test("creates a note", async () => {
const res = await request(createApp()).post("/api/notes").send({ text: "Hi" });
assert.equal(res.status, 201);
});Production checklist#
- Read
PORTand config from the environment; validate it at start-up. - Structured logs, a health endpoint, graceful shutdown on
SIGTERM. - HTTPS, Helmet, strict CORS, rate limits, validation, parameterised SQL.
npm audit, tests in CI, managed database with backups and migrations.
Want the full explanations? Go to the Learn Node.js and Express course →