Middleware — Logging, Auth, Error Handling
Middleware functions are like Swiggy delivery boys — each one checks something (token, data, auth) before passing the order to the next person in line.
Middleware is a function that runs between the request coming in and the route handler processing it. Think of it like Swiggy's delivery pipeline:
- Step 1: You place an order on Swiggy (request comes in)
- Step 2: Swiggy checks your payment (middleware 1 — auth check)
- Step 3: Swiggy assigns a delivery boy (middleware 2 — logging)
- Step 4: Restaurant prepares the food (middleware 3 — processing)
- Step 5: Food arrives! (route handler sends response)
Each middleware function either passes the request to the next (calls next()) or sends a response (short-circuits the chain).
Basic middleware structure:
import express from 'express';
const app = express();
// Middleware function
app.use((req, res, next) => {
console.log('Request received:', req.method, req.url);
next(); // Pass to the next middleware or route
});
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000);
The next() function is the most important concept. It tells Express: "I'm done with my work, pass this request to the next thing in line." If you forget to call next() AND forget to send a response, the request hangs forever!
Express comes with several built-in middleware functions. The two most common are express.json() (body parsing) and morgan (request logging).
1. express.json() — Parse Request Bodies
import express from 'express';
const app = express();
// Parses JSON bodies — without this, req.body is undefined!
app.use(express.json());
app.post('/biryani', (req, res) => {
console.log(req.body); // { name: 'Chicken Biryani', price: 250 }
res.json({ received: true });
});
2. Morgan — HTTP Request Logger
Morgan is a popular third-party middleware that logs every request to the console. Install it first:
npm install morgan
import express from 'express';
import morgan from 'morgan';
const app = express();
// Logs: POST /biryani 201 12.345 ms - 45
app.use(morgan('dev'));
// 'dev' format shows: method, url, status, response time, content-length
// 'combined' format shows: IP, method, url, status, user-agent (Apache style)
3. CORS — Cross-Origin Resource Sharing
If your frontend runs on a different port (e.g., Angular on :4200 and Express on :3000), you need CORS:
npm install cors
import cors from 'cors';
// Allow all origins (for development)
app.use(cors());
// Or restrict to specific origins:
// app.use(cors({ origin: 'http://localhost:4200' }));
Order matters! Put express.json() and morgan BEFORE your routes, so they process every request before the route handler runs.
Let's create a custom logger middleware that tracks every request — like Swiggy tracking every order from placement to delivery.
Custom request logger:
import express from 'express';
const app = express();
// Custom logger middleware — log every request
app.use((req, res, next) => {
const start = Date.now();
// Log when request starts
console.log('📩 [' + new Date().toISOString() + '] ' + req.method + ' ' + req.url);
// Listen for the response finish event
res.on('finish', () => {
const duration = Date.now() - start;
console.log('✅ [' + new Date().toISOString() + '] ' +
req.method + ' ' + req.url + ' → ' +
res.statusCode + ' (' + duration + 'ms)');
});
next(); // Crucial! Pass to the next middleware/route
});
app.get('/biryani', (req, res) => {
res.json({ biryani: 'Chicken' });
});
app.listen(3000);
What you'll see in the console:
📩 [2025-01-15T10:30:00.123Z] GET /biryani
✅ [2025-01-15T10:30:00.145Z] GET /biryani → 200 (22ms)
Middleware pattern recap:
app.use(middlewareFn)— applies to ALL routesapp.use('/biryani', middlewareFn)— applies only to routes starting with /biryani- Call
next()to pass control to the next middleware/route - Send a response (
res.json(),res.send()) to end the chain
This custom logger is exactly how tools like Morgan work internally — they intercept the request, log it, and pass it along.
Let's build an authentication middleware that checks for a valid token — like Swiggy's delivery boy checking your order token before handing over the food.
Simple auth middleware:
import express from 'express';
const app = express();
app.use(express.json());
// Fake token database (in real apps, use JWT)
const validTokens = ['swiggy-token-123', 'biryani-token-456'];
// Auth middleware — checks Authorization header
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({
success: false,
message: 'No token provided! Delivery boy ko token do bhai!'
});
}
const token = authHeader.replace('Bearer ', '');
if (!validTokens.includes(token)) {
return res.status(403).json({
success: false,
message: 'Invalid token! Yeh token to nakli hai bhai!'
});
}
// Attach user info to request for downstream use
req.user = { id: 1, name: 'Hyderabad Foodie' };
next(); // Token valid — let them through!
}
// Protected route — only accessible with valid token
app.post('/orders', authMiddleware, (req, res) => {
res.status(201).json({
success: true,
message: 'Order placed! Token verified, delivery boy on the way!',
user: req.user
});
});
// Public route — no auth needed
app.get('/menu', (req, res) => {
res.json({ items: ['Chicken Biryani', 'Mutton Biryani'] });
});
app.listen(3000);
Testing with curl:
# Without token (should get 401)
curl -X POST http://localhost:3000/orders
# With valid token (should get 201)
curl -X POST http://localhost:3000/orders \
-H "Authorization: Bearer swiggy-token-123" \
-H "Content-Type: application/json" \
-d '{"item": "Biryani", "qty": 2}'
Key points:
- Middleware can short-circuit the request by sending a response (401/403)
- Middleware can modify req/res objects (
req.user) for downstream use - Apply middleware to specific routes by passing it as the second argument
- Always call
next()if the check passes
Error handling middleware is a special type of middleware that catches errors thrown in your route handlers or other middleware. It always has four parameters: err, req, res, next.
Think of it like Swiggy's customer support — when something goes wrong (food spilled, wrong order), the customer support team handles it gracefully instead of just ghosting you.
Error handling middleware pattern:
import express from 'express';
const app = express();
// Standard middleware
app.use(express.json());
// Route that throws an error
app.get('/biryani/:id', (req, res) => {
const id = Number(req.params.id);
if (isNaN(id)) {
// Throw an error — caught by error middleware!
throw new Error('Invalid ID! Sirf numbers daalo bhai!');
}
res.json({ id, name: 'Chicken Biryani' });
});
// Route that calls next() with an error
app.post('/orders', (req, res, next) => {
if (!req.body.item) {
// Pass error to error middleware
return next(new Error('Item is required!'));
}
res.json({ status: 'ordered' });
});
// ⚡ ERROR HANDLING MIDDLEWARE — always LAST, always 4 params!
app.use((err, req, res, next) => {
console.error('💥 Error:', err.message);
console.error(err.stack); // Full stack trace for debugging
res.status(500).json({
success: false,
message: 'Kuch to gadbad hai bhai!',
error: process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message // Show details only in development
});
});
app.listen(3000);
Complete middleware stack in order:
// 1. Third-party middleware (first!)
app.use(morgan('dev'));
app.use(express.json());
app.use(cors());
// 2. Custom middleware
app.use((req, res, next) => {
console.log('Request:', req.method, req.url);
next();
});
// 3. Auth middleware (selective)
app.use('/admin', authMiddleware);
// 4. Routes
app.get('/biryani', ...);
app.post('/orders', ...);
// 5. 404 handler (catch-all)
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
// 6. Error handler (LAST!)
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Server error' });
});
Rules for error middleware:
- Must have exactly 4 parameters — Express identifies it by the arity (number of params)
- Must be placed LAST in the middleware stack
- Catches errors from
throwand fromnext(error) - Don't show stack traces in production!
Key Takeaways
- ✅ Middleware runs between request and route handler — like Swiggy delivery pipeline.
- ✅ next() passes control to the next middleware. FORGETTING next() = request hangs!
- ✅ Built-in middleware: express.json(), morgan (npm install morgan), cors (npm install cors).
- ✅ Custom middleware can modify req/res, check auth, log requests, etc.
- ✅ Apply middleware to specific routes: app.get('/path', middleware, handler).
- ✅ Error middleware has 4 params (err, req, res, next) and must be LAST.
- ✅ Order matters! Always put middleware before routes, error handler at the end.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login