Chapter 2.4☕ 17 min read

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.

01What is Middleware? — Swiggy Delivery Boy

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!

02express.json() & Morgan — Built-in Middleware

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.

03Custom Logger Middleware — Har Request Track Karna

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 routes
  • app.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.

04Auth Middleware — Token Check Karo

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
05Error Handling Middleware — Jab Kuch Phat Jaye

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 throw and from next(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.
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase