Middleware allows you to process requests and responses as they pass through your framework. It’s a powerful way to add features like logging, authentication, CORS, or response headers without modifying core logic or handlers directly.
What Middleware Is
Middleware is a function that wraps around the request/response cycle. It can modify the request before it reaches the handler or alter the response afterward.
In a lightweight framework, middleware should be simple, composable, and optional.
Designing a Middleware System
At its core, middleware can be a callable that takes two arguments:
- The
request
object - A
next_handler
function it can call to continue processing
Here’s a minimal middleware structure:
def middleware(request, next_handler):
# do something before
response = next_handler(request)
# do something after
return response
You can chain multiple middleware functions before calling the route handler.
Middleware Manager
Create a middleware manager that runs each middleware in order, passing the request along:
class App:
def __init__(self):
self.routes = {}
self.middleware = []
def use(self, mw_func):
self.middleware.append(mw_func)
def add_route(self, path, handler):
self.routes[path] = handler
def handle_request(self, path, request):
handler = self.routes.get(path)
if not handler:
return "404 Not Found"
def call_next(req, middleware_chain):
if not middleware_chain:
return handler(req)
current = middleware_chain[0]
return current(req, lambda r: call_next(r, middleware_chain[1:]))
return call_next(request, self.middleware)
Example Middleware
Logging middleware:
def logger(request, next_handler):
print(f"[LOG] {request['method']} {request['path']}")
return next_handler(request)
Authentication middleware:
def require_auth(request, next_handler):
if not request.get("user"):
return "401 Unauthorized"
return next_handler(request)
Adding Middleware and Routes
app = App()
app.use(logger)
app.use(require_auth)
def dashboard(request):
return "Welcome to your dashboard"
app.add_route("/dashboard", dashboard)
fake_request = {"method": "GET", "path": "/dashboard", "user": "alice"}
print(app.handle_request("/dashboard", fake_request))
Middleware Execution Order
Middleware is applied in the order it’s added. The first middleware runs first, and the last one wraps directly around the handler.
This allows building a clear processing pipeline.
Use Cases for Middleware
- Logging and metrics
- Authentication and permission checks
- Input validation
- Response formatting
- Error handling
Keep It Composable
To stay lightweight:
- Make middleware pure functions
- Avoid tight coupling with app internals
- Handle only what’s necessary, and pass control quickly
Wrap-Up
Middleware gives your framework modular power. It decouples concerns and helps keep route handlers focused on core logic. With just a few lines of Python, you can build a clean, flexible middleware layer for any small web project.
Want to dive deeper? Check out my 20-page PDF guide: Building a Lightweight Python Web Framework from Scratch
Top comments (0)