30  Middleware Explained

Middleware is software that sits between two systems or layers, intercepting and processing requests/responses as they flow through.

30.1 The Concept

Client Request Flow:

  Browser/Client
       │
       ↓
  ┌─────────────────────┐
  │   Middleware 1      │ ← Logging
  └─────────────────────┘
       │
       ↓
  ┌─────────────────────┐
  │   Middleware 2      │ ← Authentication
  └─────────────────────┘
       │
       ↓
  ┌─────────────────────┐
  │   Middleware 3      │ ← CORS
  └─────────────────────┘
       │
       ↓
  ┌─────────────────────┐
  │   Your Application  │ ← Business Logic
  │   (Route Handlers)  │
  └─────────────────────┘
       │
       ↓
  Response flows back up through middleware

Each middleware can: - Inspect the request - Modify the request/response - Stop the request (e.g., reject unauthorized users) - Pass it along to the next middleware

30.2 Types of Middleware by Context

30.2.1 1. Web Framework Middleware (Express.js, Flask, FastAPI)

Common examples:

Authentication Middleware
├─ Check if user is logged in
├─ Verify JWT tokens
└─ Attach user info to request

Authorization Middleware
├─ Check user permissions
├─ Verify roles (admin, user, etc.)
└─ Block unauthorized access

Logging Middleware
├─ Log every request
├─ Track response times
└─ Monitor API usage

Body Parser Middleware
├─ Parse JSON from request body
├─ Parse form data
└─ Make it accessible to your code

Error Handling Middleware
├─ Catch exceptions
├─ Format error responses
└─ Send appropriate status codes

Rate Limiting Middleware
├─ Count requests per user/IP
├─ Block excessive requests
└─ Prevent API abuse

CORS Middleware
├─ Add CORS headers
├─ Handle preflight requests
└─ Control cross-origin access

Compression Middleware
├─ Compress responses (gzip)
└─ Reduce bandwidth usage

Static File Serving
├─ Serve images, CSS, JS
└─ Cache control

30.2.2 2. JavaScript/Express Example

// Logging middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next(); // Pass to next middleware
});

// Authentication middleware
app.use((req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'No token' });
  }
  // Verify token...
  req.user = decodedUser;
  next();
});

// Your route handler (final destination)
app.get('/api/patients', (req, res) => {
  // req.user is available here because middleware added it
  res.json({ patients: [] });
});

30.2.3 3. Python/FastAPI Example

from fastapi import FastAPI, Request
import time

app = FastAPI()

# Middleware to measure request time
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)  # Process request
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

# Authentication middleware
@app.middleware("http")
async def authenticate(request: Request, call_next):
    if request.url.path.startswith("/api/"):
        token = request.headers.get("authorization")
        if not token:
            return JSONResponse(
                status_code=401,
                content={"error": "Unauthorized"}
            )
    response = await call_next(request)
    return response

30.2.4 4. Database/ORM Middleware

In systems like Prisma or SQLAlchemy:

Before Query
    │
    ↓
┌─────────────────┐
│  Logging        │ ← Log all queries
└─────────────────┘
    │
    ↓
┌─────────────────┐
│  Query Builder  │ ← Add WHERE clauses
└─────────────────┘
    │
    ↓
┌─────────────────┐
│  Connection Pool│ ← Manage DB connections
└─────────────────┘
    │
    ↓
  Database

30.2.5 5. Message Queue Middleware

Producer → [Middleware: Validation] → Queue → [Middleware: Retry Logic] → Consumer

30.3 Real-World Medical AI Example

For a DICOM image processing API:

from fastapi import FastAPI, Request, HTTPException
import logging

app = FastAPI()

# 1. Logging middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
    logger.info(f"Incoming: {request.method} {request.url}")
    response = await call_next(request)
    logger.info(f"Status: {response.status_code}")
    return response

# 2. Authentication middleware
@app.middleware("http")
async def verify_api_key(request: Request, call_next):
    if request.url.path.startswith("/api/"):
        api_key = request.headers.get("X-API-Key")
        if not is_valid_api_key(api_key):
            raise HTTPException(status_code=401, detail="Invalid API key")
    response = await call_next(request)
    return response

# 3. Rate limiting middleware
@app.middleware("http")
async def rate_limit(request: Request, call_next):
    client_ip = request.client.host
    if exceeded_rate_limit(client_ip):
        raise HTTPException(status_code=429, detail="Too many requests")
    response = await call_next(request)
    return response

# 4. Your actual endpoint
@app.post("/api/analyze-xray")
async def analyze_xray(image: UploadFile):
    # All middleware has already run
    # API key verified ✓
    # Request logged ✓
    # Rate limit checked ✓
    result = ai_model.predict(image)
    return {"findings": result}

30.4 Middleware in Different Contexts

30.4.1 Web Application Stack

                    Browser
                       ↕
              ┌─────────────────┐
              │  Reverse Proxy  │ ← Nginx/Apache middleware
              │  (SSL, Caching) │
              └─────────────────┘
                       ↕
              ┌─────────────────┐
              │  Web Framework  │ ← Express/Flask middleware
              │  (Auth, CORS)   │
              └─────────────────┘
                       ↕
              ┌─────────────────┐
              │  Application    │ ← Your code
              │  Logic          │
              └─────────────────┘
                       ↕
              ┌─────────────────┐
              │  ORM/Database   │ ← Database middleware
              │  (Connection)   │
              └─────────────────┘
                       ↕
                    Database

30.5 Key Characteristics of Middleware

  1. Order matters - Middleware executes in the order you define it
  2. Can short-circuit - Can stop the request chain
  3. Can modify context - Can add data for later middleware/handlers
  4. Reusable - Write once, use across many routes
  5. Composable - Chain multiple middleware together

30.6 When to Use Middleware

Good use cases: - ✅ Cross-cutting concerns (logging, monitoring) - ✅ Authentication/authorization - ✅ Request validation - ✅ Response transformation - ✅ Error handling

Bad use cases: - ❌ Business logic (put in route handlers) - ❌ Heavy computations (consider background jobs) - ❌ Route-specific logic (use route handlers)

The term “middleware” comes from it being “in the middle” between the client and your application logic - it’s the processing layer that requests flow through.