Basic Routing
Routing determines how your application responds to client requests at specific URLs. Express gives you a clean way to organize routes.
Route parameters capture values from the URL. Query parameters come after the ? in the URL.
// Route with parameters
app.get("/users/:id", (req, res) => {
const userId = req.params.id;
res.json({ id: userId });
});
// Query parameters
app.get("/search", (req, res) => {
const query = req.query.q;
res.json({ results: `Searching for ${query}` });
});
Try it Yourself →
Route Groups with Router
Express Router lets you group related routes together. Define routes in a separate file, export the router, and mount it on a path in your main app.
// routes/users.js
const router = require("express").Router();
router.get("/", (req, res) => {
res.json({ users: [] });
});
router.get("/:id", (req, res) => {
res.json({ id: req.params.id });
});
router.post("/", (req, res) => {
res.status(201).json({ message: "User created" });
});
module.exports = router;
// app.js
const usersRouter = require("./routes/users");
app.use("/api/users", usersRouter);
Router-Level Middleware
Middleware attached to a router runs only for routes defined on that router. You can also apply middleware to specific routes by passing it as an argument.
// Middleware that only runs for this route group
router.use((req, res, next) => {
console.log("User route accessed");
next();
});
// Route-specific middleware
router.get("/:id", authMiddleware, (req, res) => {
// This route requires auth
});
Error Handling in Routes
Express error handlers must have exactly 4 parameters (err, req, res, next). For async routes, use try/catch and pass errors to next().
// Express error handler (must have 4 parameters)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: "Something went wrong!" });
});
// Async error handling
app.get("/api/data", async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (err) {
next(err); // Pass to error handler
}
});