Express Basics
Express is a minimal web framework for Node. It handles routing, request parsing, and middleware so you do not have to.
Install and Start
npm install express
import express from "express";
const app = express();
app.use(express.json());
app.get("/", (req, res) => {
res.send("Hello, Express!");
});
// → prints the URL once the server is listening
app.listen(3000, () => console.log("http://localhost:3000"));
express.json() parses JSON bodies into req.body.
Routes
A route matches an HTTP method and a path:
app.get("/users", (req, res) => res.json([]));
app.post("/users", (req, res) => res.status(201).json(req.body));
app.put("/users/:id", (req, res) => res.json({ id: req.params.id }));
app.delete("/users/:id", (req, res) => res.status(204).end());
:idis a route parameter, read fromreq.params.- Query strings come from
req.query.
app.get("/search", (req, res) => {
res.json({ q: req.query.q ?? "" });
});
Middleware
Middleware are functions that run in order before your handler. Each one can read or change the request, or stop the chain:
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
// → GET /about (one line per request)
next(); // pass control to the next handler
}
app.use(logger);
Order matters — middleware runs top to bottom.
Sending Responses
res.send("text"); // string or Buffer
res.json({ ok: true }); // JSON + Content-Type
res.status(404).json({ error: "Not found" });
res.redirect("/login");
Error Handling
Express catches errors passed to next:
app.get("/boom", (req, res, next) => {
next(new Error("Something broke"));
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Internal Server Error" });
});
The error handler must have four arguments.
Full example
A small but complete REST API: a separate app and server, a users router, shared data, and a logger middleware.
{
"name": "users-api",
"type": "module",
"scripts": {
"dev": "node --watch src/server.js",
"start": "node src/server.js"
},
"dependencies": {
"express": "^5.0.0"
}
}
export const users = [
{ id: 1, name: "Ada", role: "admin" },
{ id: 2, name: "Linus", role: "user" },
];
let nextId = 3;
export function listUsers() {
return users;
}
export function getUser(id) {
return users.find((user) => user.id === Number(id));
}
export function createUser({ name, role = "user" }) {
const user = { id: nextId++, name, role };
users.push(user);
return user;
}
export function removeUser(id) {
const index = users.findIndex((user) => user.id === Number(id));
if (index === -1) return false;
users.splice(index, 1);
return true;
}
export function logger(req, res, next) {
const start = Date.now();
res.on("finish", () => {
console.log(
`${req.method} ${req.originalUrl} ${res.statusCode} — ${Date.now() - start}ms`
);
});
next();
}
import { Router } from "express";
import { listUsers, getUser, createUser, removeUser } from "../data/users.js";
export const usersRouter = Router();
usersRouter.get("/", (req, res) => {
res.json(listUsers());
});
usersRouter.get("/:id", (req, res) => {
const user = getUser(req.params.id);
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
});
usersRouter.post("/", (req, res) => {
const { name } = req.body ?? {};
if (!name) return res.status(400).json({ error: "name is required" });
res.status(201).json(createUser(req.body));
});
usersRouter.delete("/:id", (req, res) => {
if (!removeUser(req.params.id)) {
return res.status(404).json({ error: "User not found" });
}
res.status(204).end();
});
import express from "express";
import { logger } from "./middleware/logger.js";
import { usersRouter } from "./routes/users.js";
export const app = express();
app.use(express.json());
app.use(logger);
app.get("/health", (req, res) => res.json({ ok: true }));
app.use("/api/users", usersRouter);
// 404 for anything unmatched.
app.use((req, res) => {
res.status(404).json({ error: "Not found" });
});
// Central error handler — must take four arguments.
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Internal Server Error" });
});
import { app } from "./app.js";
const port = process.env.PORT ?? 3000;
app.listen(port, () => {
console.log(`API listening on http://localhost:${port}`);
});
Best Practices
- Organize routes with
express.Router()— Keep files small. - Validate input — Never trust
req.body. - Use a central error handler — One place to log and respond.
- Add security middleware —
helmet, CORS, rate limits.
Common Mistakes
- Forgetting
express.json()—req.bodyisundefined. - Putting routes after the 404 handler — They never run.
- Three-argument error handlers — Express needs four.
- Not calling
next()— The request hangs.