Express earns its popularity by keeping the core small while still covering everything a typical backend needs.
1. Minimalist by design
A working server takes only a few lines.
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Bookstore API is live");
});
app.listen(4000, () => {
console.log("Server is running on http://localhost:4000");
});
No boilerplate, no forced project structure — just a route and a listener.
2. A flexible routing system
Different HTTP verbs, different handlers, same simple syntax.
app.get("/books", (req, res) => {
res.send("List of books");
});
app.post("/books", (req, res) => {
res.send("Book added");
});
3. Middleware support
Middleware functions intercept the request/response cycle — globally or per route.
app.use((req, res, next) => {
console.log("Incoming request at:", Date.now());
next();
});
app.get("/dashboard", (req, res) => {
res.send("Dashboard loaded");
});
4. Template engine support
Express can render dynamic HTML using engines like EJS, Pug, or Handlebars.
npm install ejs
views/summary.ejs:
<h1>Reading Summary</h1>
<p>Reader: <%= reader.name %></p>
<p>Books finished: <%= reader.count %></p>
server.js:
const express = require("express");
const app = express();
app.set("view engine", "ejs");
app.get("/summary", (req, res) => {
const reader = { name: "Nadia", count: 12 };
res.render("summary", { reader });
});
app.listen(4000, () => console.log("Server running on http://localhost:4000"));
5. Asynchronous request handling
Built on Node's non-blocking model, so Express can serve many requests concurrently.
app.get("/inventory", async (req, res) => {
try {
const data = await fetchInventory();
res.json(data);
} catch (err) {
res.status(500).send("Failed to load inventory");
}
});
async function fetchInventory() {
return { id: 7, title: "Bookstore Stock Report" };
}
Why choose Express
- Lightweight without giving up flexibility.
- A gentle learning curve, with a huge ecosystem of add-ons.
- Fast to build routes, middleware, and request handling.
- Rich middleware support for auth, logging, validation, security, uploads.
- One of the most common choices for REST APIs.
- Inherits Node's async, event-driven performance.
Common use cases
- Building REST APIs
- Backend for React/Angular/Vue/Next.js frontends
- Serving static assets
- Session/JWT/OAuth-based authentication
- Real-time apps paired with Socket.IO
- Lightweight microservices
- Acting as an API gateway