Middleware composition helps organize multiple small functions that check user identity and permissions. It keeps your code clean and easy to manage.
Middleware composition for auth layers in Express
Start learning this pattern below
Jump into concepts and practice - no test required
or
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Syntax
Express
app.use(middleware1, middleware2, middleware3); // or for a single route app.get('/path', middleware1, middleware2, handler);
Each middleware function receives req, res, and next to pass control.
Order matters: middleware runs in the order you list them.
Examples
Express
function checkLoggedIn(req, res, next) {
if (req.user) next();
else res.status(401).send('Not logged in');
}
function checkAdmin(req, res, next) {
if (req.user && req.user.role === 'admin') next();
else res.status(403).send('Admin only');
}
app.get('/admin', checkLoggedIn, checkAdmin, (req, res) => {
res.send('Welcome Admin');
});Express
const authMiddleware = [checkLoggedIn, checkAdmin]; app.get('/admin', ...authMiddleware, (req, res) => { res.send('Welcome Admin'); });
Sample Program
This Express app sets a fake user, then uses two middleware functions to check if the user is logged in and is an admin before showing a welcome message.
Express
import express from 'express'; const app = express(); // Simulate user data app.use((req, res, next) => { req.user = { name: 'Alice', role: 'admin' }; next(); }); function checkLoggedIn(req, res, next) { if (req.user) next(); else res.status(401).send('Not logged in'); } function checkAdmin(req, res, next) { if (req.user && req.user.role === 'admin') next(); else res.status(403).send('Admin only'); } app.get('/admin', checkLoggedIn, checkAdmin, (req, res) => { res.send(`Welcome Admin ${req.user.name}`); }); app.listen(3000, () => console.log('Server running on http://localhost:3000'));
Important Notes
Middleware functions must call next() to continue the chain.
Always handle errors or send responses to avoid hanging requests.
Use middleware composition to keep your auth logic reusable and clear.
Summary
Middleware composition lets you run multiple auth checks in order.
Use small middleware functions for each auth step.
Group middleware for cleaner route definitions.
Practice
1. What is the main purpose of composing multiple middleware functions for authentication in Express?
easy
Solution
Step 1: Understand middleware composition
Middleware composition means running multiple middleware functions one after another.Step 2: Purpose in auth layers
Using multiple small auth checks in order helps keep code clean and checks each condition separately.Final Answer:
To run several small auth checks in order before allowing access -> Option AQuick Check:
Middleware composition = multiple small auth checks [OK]
Hint: Think of middleware as a chain of small checks [OK]
Common Mistakes:
- Thinking all auth logic must be in one function
- Believing middleware skips auth
- Confusing middleware with database queries
2. Which of the following is the correct way to apply two middleware functions
checkToken and checkRole to an Express route using an array?easy
Solution
Step 1: Understand Express middleware syntax
Express accepts multiple middleware as an array or separate arguments before the handler. This question specifies using an array.Step 2: Check each option
A uses separate arguments. B and D use logical operators which are invalid here. C correctly uses an array.Final Answer:
app.get('/admin', [checkToken, checkRole], (req, res) => res.send('OK')) -> Option CQuick Check:
Middleware array syntax = app.get('/admin', [checkToken, checkRole], (req, res) => res.send('OK')) [OK]
Hint: Use arrays to group middleware in routes [OK]
Common Mistakes:
- Using logical operators instead of arrays
- Passing middleware as a single combined expression
- Forgetting to include middleware before handler
3. Given the middleware functions below, what will be the response when a request with
req.user = { role: 'user' } hits the route?function checkToken(req, res, next) {
if (!req.user) return res.status(401).send('No token');
next();
}
function checkAdmin(req, res, next) {
if (req.user.role !== 'admin') return res.status(403).send('Forbidden');
next();
}
app.get('/secure', [checkToken, checkAdmin], (req, res) => res.send('Welcome admin'));medium
Solution
Step 1: Analyze checkToken middleware
It checks if req.user exists. Here req.user is { role: 'user' }, so it passes and calls next().Step 2: Analyze checkAdmin middleware
It checks if req.user.role is 'admin'. Here it is 'user', so it returns 403 Forbidden response.Final Answer:
Forbidden -> Option BQuick Check:
Role check fails = Forbidden [OK]
Hint: Check middleware order and conditions carefully [OK]
Common Mistakes:
- Assuming role 'user' passes admin check
- Ignoring middleware that sends response early
- Confusing status codes
4. Identify the error in this middleware composition code:
function auth(req, res, next) {
if (!req.headers.authorization) {
res.status(401).send('Unauthorized');
}
next();
}
app.get('/data', auth, (req, res) => res.send('Data'));medium
Solution
Step 1: Check middleware flow
If authorization header is missing, it sends 401 but does not stop execution.Step 2: Identify missing return
Without return after res.status(401).send(), next() is called anyway, causing route handler to run incorrectly.Final Answer:
Missing return after sending 401 response, so next() runs anyway -> Option AQuick Check:
Send response must stop middleware with return [OK]
Hint: Always return after sending response in middleware [OK]
Common Mistakes:
- Calling next() after sending response
- Thinking async needed for simple middleware
- Putting auth logic in route handler
5. You want to create a reusable middleware group for routes that require both token validation and admin role check. Which is the best way to compose and apply these middlewares in Express?
hard
Solution
Step 1: Understand middleware grouping
Grouping middlewares as an array keeps each check separate and reusable.Step 2: Compare options
Use an array of separate middlewares and apply the array to routes uses an array of middlewares applied to routes, which is clean and composable. Create a single middleware combining both checks and use it in routes merges checks into one, losing modularity. Call each middleware manually inside the route handler function is manual and error-prone. Use a global app.use() for all routes regardless of auth needs applies auth globally, which is not selective.Final Answer:
Use an array of separate middlewares and apply the array to routes -> Option DQuick Check:
Middleware arrays = reusable and clean [OK]
Hint: Group middlewares in arrays for reuse [OK]
Common Mistakes:
- Combining all logic into one middleware
- Calling middleware inside handlers manually
- Applying auth globally without route control
