`/users/:userId/posts` — Resources Inside Resources
Nested Routes
Resource hierarchies — comments on posts on users — modeled with nested routers.
What you'll learn
- Mount nested routers
- Share params with mergeParams
- Decide when to nest vs flatten
Real APIs have hierarchies: a post belongs to a user, comments belong to posts. You can model this with nested routers — but tread carefully.
Nesting With mergeParams
// src/routes/user-posts.js
import { Router } from "express";
const r = Router({ mergeParams: true }); // ← important
r.get("/", (req, res) => {
res.json({ userId: req.params.userId });
});
r.get("/:postId", (req, res) => {
res.json({ userId: req.params.userId, postId: req.params.postId });
});
export default r; // src/app.js
import userPosts from "./routes/user-posts.js";
app.use("/api/users/:userId/posts", userPosts); GET /api/users/42/posts→{ userId: "42" }GET /api/users/42/posts/7→{ userId: "42", postId: "7" }
mergeParams: true propagates the parent’s params into the inner
router. Without it, req.params.userId is undefined.
When To Nest
- The child only ever belongs to the parent (comments on posts)
- The URL hierarchy reads naturally
- The child can’t exist without the parent
When NOT To Nest
- Items belong to multiple parents (a tag belongs to many posts)
- You frequently look up by child ID alone (
/posts/42instead of/users/8/posts/42) - The hierarchy is more than 2 levels deep — URLs become unreadable
Nesting more than 2 levels (/users/:u/posts/:p/comments/:c/replies/:r)
is almost always a mistake. Flatten.
The Flat Alternative
/users ← list / create users
/users/:id ← read / update / delete
/posts ← list / create posts (with ?author=:userId filter)
/posts/:id ← read / update / delete
/posts/:id/comments ← only one level deep Many real APIs prefer flat — comments and posts are top-level,
filtered by query params (?postId=7). Simpler client code; the
parent relation isn’t in the URL but in the body or query.
Verdict
For 2-level hierarchies that read naturally, nest. For everything else, prefer flat. Hierarchies that mostly make sense in your database schema don’t always make good URLs.
Multiple Route Handlers →