mediumBackend EngineerProduct
How would you structure Express middleware for authentication, error handling, and request logging in a production API?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a product company interview:
> "Our Express app has 30 routes. Auth checks are duplicated in each handler, error handling is inconsistent, and we have no request logging. How would you implement a clean middleware architecture?"
> "Our Express app has 30 routes. Auth checks are duplicated in each handler, error handling is inconsistent, and we have no request logging. How would you implement a clean middleware architecture?"
Suggested Solution
Production Middleware Architecture
import express from "express";
import cors from "cors";
import helmet from "helmet";
import { logger } from "./lib/logger";
import { verifyToken } from "./lib/auth";
const app = express();
// ═══════════════════════════════════
// Layer 1: Global Middleware (ALL requests)
// ═══════════════════════════════════
app.use(helmet()); // Security headers
app.use(cors({ origin: process.env.ALLOWEDORIGINS }));
app.use(express.json({ limit: "1mb" }));
// Request logging
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
const duration = Date.now() - start;
logger.info({
method: req.method,
path: req.path,
status: res.statusCode,
duration: ${duration}ms,
ip: req.ip,
userAgent: req.get("user-agent"),
});
});
next();
});
// ═══════════════════════════════════
// Layer 2: Public Routes (no auth)
// ═══════════════════════════════════
app.use("/api/auth", authRoutes);
app.use("/api/public", publicRoutes);
// ═══════════════════════════════════
// Layer 3: Auth Middleware (protected routes)
// ═══════════════════════════════════
app.use("/api", authenticate);
// Role-based middleware
app.use("/api/admin", requireRole("admin"));
app.use("/api/premium", requirePlan("premium"));
// ═══════════════════════════════════
// Layer 4: Business Routes
// ═══════════════════════════════════
app.use("/api/users", userRoutes);
app.use("/api/applications", applicationRoutes);
// ═══════════════════════════════════
// Layer 5: Error Handling (LAST)
// ═══════════════════════════════════
app.use(notFoundHandler);
app.use(errorHandler);
Auth Middleware
function authenticate(req: AuthRequest, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token) {
return res.status(401).json({ error: "No token provided" });
}
try {
const payload = verifyToken(token);
req.user = { id: payload.sub, email: payload.email, role: payload.role };
next();
} catch (err) {
if (err.name === "TokenExpiredError") {
return res.status(401).json({ error: "Token expired" });
}
return res.status(401).json({ error: "Invalid token" });
}
}
function requireRole(...roles: string[]) {
return (req: AuthRequest, res: Response, next: NextFunction) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: "Insufficient permissions" });
}
next();
};
}
Error Handler
class AppError extends Error {
constructor(public statusCode: number, public message: string, public isOperational = true) {
super(message);
}
}
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({ error: err.message });
}
// Unexpected errors
logger.error("Unhandled error", { error: err.stack });
res.status(500).json({
error: process.env.NODEENV === "production"
? "Internal server error"
: err.message,
});
}