What if you could secure your app with simple building blocks instead of repeating code everywhere?
Why Middleware composition for auth layers in Express? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you have to check if a user is logged in, then verify their role, and finally confirm their permissions for every single request by writing all these checks inside each route handler.
Doing all these checks manually in every route is repetitive, easy to forget, and makes your code messy and hard to maintain. If you want to change one check, you must update many places, risking bugs.
Middleware composition lets you build small, reusable functions for each auth step and combine them easily. This way, your routes stay clean, and auth logic is centralized and consistent.
app.get('/dashboard', (req, res) => { if (!req.user) return res.status(401).send('Login required'); if (req.user.role !== 'admin') return res.status(403).send('Forbidden'); // ...rest of handler });
const compose = (middlewares) => (req, res, next) => {
let index = 0;
const run = () => {
if (index < middlewares.length) {
middlewares[index++](req, res, run);
} else {
next();
}
};
run();
};
const auth = compose([checkLoggedIn, checkAdminRole]);
app.get('/dashboard', auth, (req, res) => {
// ...rest of handler
});You can build clear, maintainable, and secure auth layers by combining simple middleware functions that run in order automatically.
Think of a club entrance where a bouncer first checks your ID, then your membership card, and finally your VIP pass before letting you in. Middleware composition is like having each bouncer handle one check smoothly in a line.
Manual auth checks clutter code and cause errors.
Middleware composition breaks auth into reusable steps.
It keeps routes clean and security consistent.
Practice
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]
- Thinking all auth logic must be in one function
- Believing middleware skips auth
- Confusing middleware with database queries
checkToken and checkRole to an Express route using an array?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]
- Using logical operators instead of arrays
- Passing middleware as a single combined expression
- Forgetting to include middleware before handler
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'));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]
- Assuming role 'user' passes admin check
- Ignoring middleware that sends response early
- Confusing status codes
function auth(req, res, next) {
if (!req.headers.authorization) {
res.status(401).send('Unauthorized');
}
next();
}
app.get('/data', auth, (req, res) => res.send('Data'));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]
- Calling next() after sending response
- Thinking async needed for simple middleware
- Putting auth logic in route handler
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]
- Combining all logic into one middleware
- Calling middleware inside handlers manually
- Applying auth globally without route control
