You ask AI to add authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. to your Express APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.. It generates an authMiddleware function and adds it to your app. But it places it after the routes that need protection, so every request bypasses authentication entirely. The code looks correct. The structure is wrong.
What middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it. actually is
Middleware is a function that runs during the lifecycle of a request, after Express receives it and before your route handlerWhat is route handler?A Next.js file named route.js inside the app/ directory that handles HTTP requests directly - the App Router equivalent of API routes. sends a response. Think of it as a pipelineWhat is pipeline?A sequence of automated steps (install, lint, test, build, deploy) that code passes through before reaching production.: the request flows through each middleware function in order, and each one can read, modify, or reject the request before passing it along.
// A simple logging middleware
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next(); // Pass control to the next middleware or route
}
app.use(logger); // Register it for ALL routesThe key is next(). If a middleware does not call next(), the request stops there. The client waits forever and eventually times out.
| Component | Role |
|---|---|
req | The incoming request object (headers, body, URL, etc.) |
res | The response object (used to send data back to the client) |
next() | Passes control to the next middleware in the chain |
next(err) | Passes an error to the error-handling middleware |
The middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it. chain
Middleware runs in the order you register it with app.use(). This order is everything.
const express = require('express');
const app = express();
// 1. Parse JSON bodies (must come before routes that read req.body)
app.use(express.json());
// 2. Log every request
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
next();
});
// 3. Check authentication
app.use('/api', (req, res, next) => {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ error: 'No token' });
next();
});
// 4. Routes
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});If you move express.json() below the routes, req.body is undefined in every POST handler. If you move the auth middleware below the routes, every request passes through without authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token.. The code has no errors. The behavior is completely wrong.
Built-in middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it.
Express ships with three built-in middleware functions that cover the most common needs.
// Parse JSON request bodies (Content-Type: application/json)
app.use(express.json());
// Parse URL-encoded form data (Content-Type: application/x-www-form-urlencoded)
app.use(express.urlencoded({ extended: true }));
// Serve static files from the "public" directory
app.use(express.static('public'));| Middleware | What it does | When you need it |
|---|---|---|
express.json() | Parses JSON in request body, populates req.body | Any API that accepts JSON |
express.urlencoded() | Parses form submissions, populates req.body | HTML form handling |
express.static() | Serves files directly from a directory | Images, CSS, client-side JS |
Without express.json(), sending a POST request with a JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it. body results in req.body being undefined. This is the number one "it works in Postman but not in my code" debugging scenario.
Writing custom middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it.
Custom middleware follows the same pattern: receive req, res, next, do something, call next().
Request timing
function requestTimer(req, res, next) {
const start = Date.now();
// Run after the response is sent
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
}
app.use(requestTimer);AuthenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. middleware
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1]; // "Bearer <token>"
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
try {
const user = verifyToken(token);
req.user = user; // Attach user to request for downstream handlers
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Apply only to /api routes
app.use('/api', authenticate);
// This route can access req.user because authenticate runs first
app.get('/api/profile', (req, res) => {
res.json({ user: req.user });
});Notice the return before res.status(401). Without it, next() would still be called after sending the 401 response, causing the route handlerWhat is route handler?A Next.js file named route.js inside the app/ directory that handles HTTP requests directly - the App Router equivalent of API routes. to execute anyway.
Middleware that modifies the request
A powerful pattern is middleware that attaches data to req so later handlers can use it.
function loadUser(req, res, next) {
if (req.params.userId) {
req.user = database.findUser(req.params.userId);
if (!req.user) {
return res.status(404).json({ error: 'User not found' });
}
}
next();
}
app.get('/users/:userId', loadUser, (req, res) => {
// req.user is guaranteed to exist here
res.json(req.user);
});You can also pass middleware directly to a route instead of using app.use(). This applies it only to that specific route.
Error-handling middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it.
Error-handling middleware has a special signature: four arguments instead of three. Express only calls it when next(err) is called or when an error is thrown.
// Regular middleware - 3 args
app.use((req, res, next) => {
next();
});
// Error middleware - 4 args (err comes first)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Internal server error'
});
});The four-argument signature is not a convention, it is how Express identifies error handlers. If you accidentally omit next from the parameter list, Express treats it as regular middleware and it never receives errors.
(err, req, res), dropping next. Express sees three parameters and treats it as regular middleware, not error middleware. The error handler never fires, and unhandled errors crash your server or return default HTML error pages.// WRONG: Express sees 3 args, treats as regular middleware
app.use((err, req, res) => {
res.status(500).json({ error: err.message });
});
// CORRECT: Express sees 4 args, treats as error middleware
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});Error middleware must be registered after all routes. If you put it before your routes, it will not catch errors from those routes.
Triggering error middleware
app.get('/books/:id', (req, res, next) => {
try {
const book = findBook(req.params.id);
if (!book) {
const err = new Error('Book not found');
err.status = 404;
return next(err); // Passes to error middleware
}
res.json(book);
} catch (err) {
next(err); // Passes unexpected errors to error middleware
}
});Common middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it. ordering
A typical Express app registers middleware in this order. Deviating from it causes subtle bugs.
| Order | Middleware | Why this position |
|---|---|---|
| 1 | express.json() | Body must be parsed before any handler reads it |
| 2 | express.urlencoded() | Same reason, form data parsing |
| 3 | CORS middleware | Must set headers before any response is sent |
| 4 | Logging middleware | Log every request including failed auth |
| 5 | Authentication | Reject unauthorized requests before hitting routes |
| 6 | Route handlers | Business logic |
| 7 | 404 handler | Catch requests that matched no route |
| 8 | Error handler (4 args) | Catch all errors from above |