Express.js Guide — Complete Web Framework for Node.js
Express is a minimal, unopinionated web framework for Node.js — it provides a thin layer of routing, middleware, static file serving, and template rendering without obscuring Node.js features.
What You’ll Learn
By the end of this tutorial, you’ll build a RESTful API with Express, use middleware for logging and error handling, serve static files, structure a real-world application, and implement security best practices.
Why Express.js Matters
Express is the most popular Node.js framework — used by companies like IBM, Uber, and MySpace. At DodaTech, Doda Browser’s bookmark sync API and DodaZIP’s file conversion endpoints are built with Express. Its minimal design means you have complete control over your application architecture while benefiting from a vast ecosystem of middleware packages.
Express.js Learning Path
flowchart LR
A[Core Modules] --> B[Express.js]
B --> C[Database]
C --> D[Advanced]
B --> E{You Are Here}
style E fill:#f90,color:#fff
What is Express? (The “Why” First)
Think of Express as the plumbing for your web application. The raw http module in Node.js is like having water pipes but no faucets — functional but not user-friendly. Express adds the faucets (routing), the water filters (middleware), and the fixtures (static file serving, template rendering) that make building web applications practical.
Setup — Your First Express Server
npm init -y
npm install express
npm install -D nodemon # Auto-restart during development// server.js
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (req, res) => {
res.send("Hello Express!");
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});// package.json scripts
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}Line by line:
express()— creates the application objectapp.get("/", callback)— handles GET requests to/res.send(...)— sends a response (auto-detects content type)app.listen(port, callback)— starts the server
Routing — Mapping URLs to Code
// HTTP methods
app.get("/users", (req, res) => res.json({ users: [] }));
app.post("/users", (req, res) => res.status(201).json({ id: 1 }));
app.put("/users/:id", (req, res) => res.json({ id: req.params.id }));
app.delete("/users/:id", (req, res) => res.status(204).send());
// Route parameters
app.get("/users/:userId/books/:bookId", (req, res) => {
res.json(req.params); // { userId: "42", bookId: "5" }
});
// Query parameters
app.get("/search", (req, res) => {
res.json(req.query); // /search?q=express&page=2
});
// Express Router — modular routes
const router = express.Router();
router.get("/", (req, res) => res.json({ message: "Users list" }));
router.get("/:id", (req, res) => res.json({ id: req.params.id }));
app.use("/api/users", router);Middleware — The Request Pipeline
Middleware functions run in sequence during the request-response cycle. Each can modify the request/response or end the request:
const app = express();
// Built-in middleware
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse form data
app.use(express.static("public")); // Serve static files
// Custom logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
next(); // Pass control to the next middleware
});
// Route-specific middleware
function requireAuth(req, res, next) {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ error: "Unauthorized" });
req.user = { id: 1, name: "Alice" };
next();
}
app.get("/profile", requireAuth, (req, res) => {
res.json({ user: req.user });
});
// Third-party middleware
const cors = require("cors");
const helmet = require("helmet");
const morgan = require("morgan");
app.use(helmet());
app.use(cors());
app.use(morgan("dev"));Middleware order matters — express.json() must come before route handlers. Error handlers must be last.
Error Handling
// 404 handler — after all routes
app.use((req, res) => {
res.status(404).json({ error: "Route not found" });
});
// Global error handler — 4 params
app.use((err, req, res, next) => {
console.error("Error:", err.stack);
res.status(err.status || 500).json({
error: process.env.NODE_ENV === "production"
? "Internal server error"
: err.message
});
});
// Async error wrapper (Express 5 handles this natively)
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}Express 4 doesn’t catch promise rejections automatically. The asyncHandler wrapper ensures async errors reach the error handler.
REST API Example — Complete CRUD
const express = require("express");
const app = express();
app.use(express.json());
let books = [
{ id: 1, title: "1984", author: "George Orwell", year: 1949 },
{ id: 2, title: "Brave New World", author: "Aldous Huxley", year: 1932 }
];
let nextId = 3;
app.get("/api/books", (req, res) => {
let result = books;
if (req.query.author) {
result = books.filter(b =>
b.author.toLowerCase().includes(req.query.author.toLowerCase())
);
}
res.json(result);
});
app.get("/api/books/:id", (req, res) => {
const book = books.find(b => b.id === Number(req.params.id));
if (!book) return res.status(404).json({ error: "Not found" });
res.json(book);
});
app.post("/api/books", (req, res) => {
const { title, author, year } = req.body;
if (!title || !author) return res.status(400).json({ error: "Title and author required" });
const book = { id: nextId++, title, author, year };
books.push(book);
res.status(201).json(book);
});
app.put("/api/books/:id", (req, res) => {
const book = books.find(b => b.id === Number(req.params.id));
if (!book) return res.status(404).json({ error: "Not found" });
Object.assign(book, req.body, { id: Number(req.params.id) });
res.json(book);
});
app.delete("/api/books/:id", (req, res) => {
const index = books.findIndex(b => b.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: "Not found" });
books.splice(index, 1);
res.status(204).send();
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Internal server error" });
});
app.listen(3000);Common Mistakes
1. Middleware order — express.json() must be before routes. Error handlers must be after all routes.
2. Forgetting next() — Middleware that doesn’t call next() hangs the request.
3. Not handling async errors — Express 4 doesn’t catch promise rejections. Use try/catch or asyncHandler.
4. Sending multiple responses — Always return early when sending error responses to prevent double-send.
5. Exposing stack traces in production — Check NODE_ENV before sending error details.
Practice Questions
1. What is middleware in Express?
Functions that execute during the request-response cycle — they can modify req/res, end the request, or call the next middleware.
2. How do you parse JSON request bodies?
Use express.json() middleware. It parses JSON payloads and makes them available as req.body.
3. Difference between app.use and app.get?
app.use applies to all HTTP methods. app.get applies only to GET requests. Both accept an optional path prefix.
4. How do you structure a large Express app?
Use express.Router() for modular routes. Keep business logic in separate service modules. Use middleware for cross-cutting concerns.
5. Challenge: Add input validation middleware to the Book API POST endpoint.
function validateBook(req, res, next) {
const { title, author } = req.body;
if (!title || !author) {
return res.status(400).json({ error: "Title and author required" });
}
next();
}
app.post("/api/books", validateBook, (req, res) => { /* ... */ });FAQ
Try It Yourself
Run the Book API server and test endpoints:
curl http://localhost:3000/api/books
curl -X POST http://localhost:3000/api/books \
-H "Content-Type: application/json" \
-d '{"title":"Dune","author":"Frank Herbert","year":1965}'What’s Next
| Lesson | Description |
|---|---|
| https://tutorials.dodatech.com/backend/nodejs/nodejs-database/ | Database integration |
| https://tutorials.dodatech.com/backend/nodejs/nodejs-advanced/ | Clustering, WebSockets |
| https://tutorials.dodatech.com/backend/nodejs/koa/ | Koa.js alternative framework |
| MongoDB | MongoDB with Mongoose |
| REST API | RESTful API design |
What’s Next
Congratulations on completing this Express tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro