Remember this
Middleware sits between the incoming request and your route handler. It can run code, change req or res, send a response, or pass control forward.
If it does not send a response, it must call next(). Otherwise the browser, Postman, or curl keeps waiting.
Where it runs
Express has application-level middleware and router-level middleware. You use app.use() for the whole app, or router.use() for one group of routes.
In a recruiter-ready repo, middleware is useful for logging, simple checks, auth later, and parsing request bodies. This lesson focuses on flow, not body parsing.
A safe logger
This logger prints the method and path, then lets the request continue. Put it before your routes.
Notice the next() call. Without it, GET /health never reaches the route.
import express from 'express';
const app = express();
function requestLogger(req, res, next) {
console.log(`${req.method} ${req.path}`);
next();
}
app.use(requestLogger);
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(3000, () => {
console.log('API running on port 3000');
});Add one middleware
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
The hanging request
The most common bug is writing middleware that neither responds nor calls next(). Your server is still running, but the request is stuck.
This often happens when you add an if check and forget the else path.
function brokenCheck(req, res, next) {
if (req.headers['x-student-id']) {
next();
}
// No response and no next() when the header is missing.
}
function fixedCheck(req, res, next) {
if (!req.headers['x-student-id']) {
return res.status(400).json({ error: 'x-student-id header is required' });
}
next();
}Use return
When middleware sends a response, use return. It stops the function there and avoids accidentally calling next() later.
This keeps your code clear during hackathon debugging, especially when you add checks quickly.
function requireCollege(req, res, next) {
if (!req.headers['x-college']) {
return res.status(400).json({ error: 'x-college header is required' });
}
req.college = req.headers['x-college'];
next();
}Router-level middleware
Use router-level middleware when only one group of routes needs a check. For example, project routes may require a team id, but health routes should stay open.
This helps your project structure look intentional.
import express from 'express';
const app = express();
const projectsRouter = express.Router();
function requireTeamId(req, res, next) {
if (!req.headers['x-team-id']) {
return res.status(400).json({ error: 'x-team-id header is required' });
}
req.teamId = req.headers['x-team-id'];
next();
}
projectsRouter.use(requireTeamId);
projectsRouter.get('/', (req, res) => {
res.json({ teamId: req.teamId, projects: [] });
});
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.use('/projects', projectsRouter);
app.listen(3000);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