Exercises
Objectives
By the end of this lesson, you should be able to:
- Build an ownership-checked edit route
- Build a role-checked admin route
- Confirm both correctly distinguish authentication failures from authorization failures
⚠️ A note on verification: every command and every output in this lesson was actually run, with real HTTP requests against a real Express app.
Exercise: Posts and an Admin Panel
a) Build an edit route. PUT /api/v1/posts/:id, following Lesson 3’s ownership pattern, isOwner || isAdmin, otherwise 403:
app.put('/api/v1/posts/:id', requireAuth, (req, res) => {
const post = posts.find(p => p.id === Number(req.params.id));
if (!post) return res.status(404).json({ error: 'NotFound', message: 'Post not found' });
const isOwner = post.authorId === req.user.userId;
const isAdmin = req.user.role === 'admin';
if (!isOwner && !isAdmin) {
return res.status(403).json({ error: 'Forbidden', message: 'You can only edit your own posts' });
}
post.title = req.body.title;
res.json(post);
});
b) Build an admin-only route. GET /api/v1/admin/users, following Lesson 2’s role pattern, listing every user, requireAuth and requireRole('admin'):
app.get('/api/v1/admin/users', requireAuth, requireRole('admin'), (req, res) => {
res.json(users.map(u => ({ id: u.id, email: u.email, role: u.role })));
});
c) Test editing someone else’s post:
JORDAN EDITS ERIN POST: 403 {"error":"Forbidden","message":"You can only edit your own posts"}
d) Test editing your own post:
JORDAN EDITS OWN POST: 200 {"id":2,"title":"Updated title","authorId":2}
e) Test a non-admin listing users:
MEMBER LISTS USERS: 403 {"error":"Forbidden","message":"Requires admin role"}
f) Test an admin listing users:
ADMIN LISTS USERS: 200 [{"id":1,"email":"erin@example.com","role":"member"}, ...]
g) A permissions-based extension. Roles work well until requirements get more specific (“editors can publish posts, but not delete users”). Sketch, in words or in a comment, what a permissions: ['posts:edit', 'posts:delete'] array on a user, checked with a requirePermission('posts:delete') middleware, would look like, and explain one advantage it has over a single role string for a larger application.
Recap
This module covered authorization as a distinct concern from authentication: the difference between the two, role-based middleware for broad permissions, and ownership-based checks for “can this specific user act on this specific resource,” a pattern that shows up constantly in real CRUD APIs.
Next module: input validation, making sure the data reaching every one of these routes is valid before any of this logic even runs.