The core rule
Protect the server, not just the screen. Hiding a button in React is useful for UX, but it does not stop someone from calling your API with Postman or curl.
Every private route must check who the user is before it reads or writes data.
What to protect
Protect two things: routes and records. A route check asks, “Is anyone signed in?” A record check asks, “Does this signed-in user own this data or have this role?”
For example, /dashboard needs a signed-in user. /api/orders/123 also needs to confirm that order 123 belongs to that user.
Route protection flow
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done
Express middleware
This example assumes an earlier lesson already stored userId in the session after sign-in. The important part is that every private API route uses requireUser before doing work.
function requireUser(req, res, next) {
if (!req.session || !req.session.userId) {
return res.status(401).json({ error: "Sign in required" });
}
req.user = { id: req.session.userId };
next();
}
app.get("/api/me", requireUser, async (req, res) => {
const user = await db.user.findUnique({
where: { id: req.user.id },
select: { id: true, name: true, email: true }
});
res.json({ user });
});
app.get("/api/projects/:id", requireUser, async (req, res) => {
const project = await db.project.findFirst({
where: { id: req.params.id, ownerId: req.user.id },
select: { id: true, name: true, description: true }
});
if (!project) {
return res.status(404).json({ error: "Project not found" });
}
res.json({ project });
});Common leak
A common mistake is to fetch by id only, then check the owner later or not at all. That creates an IDOR bug, where a user changes /api/projects/1 to /api/projects/2 and sees someone else’s data.
Filter by both record id and owner id in the database query when you can. It is simple and harder to forget.
Use AI carefully
You can ask ChatGPT, Gemini, or Claude to review your route protection. Paste one route at a time and ask: “Can a signed-in user access another user’s records here?”
Check the answer yourself. AI may miss framework details, but it is good at spotting missing ownerId filters, missing middleware, and over-returned fields like passwordHash.
Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done