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 response30.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
- Order matters - Middleware executes in the order you define it
- Can short-circuit - Can stop the request chain
- Can modify context - Can add data for later middleware/handlers
- Reusable - Write once, use across many routes
- 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.