DEV Community

Cover image for Building Middleware Support in a Lightweight Python Web Framework
HexShift
HexShift

Posted on

Building Middleware Support in a Lightweight Python Web Framework

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Example Middleware

Logging middleware:

def logger(request, next_handler):
    print(f"[LOG] {request['method']} {request['path']}")
    return next_handler(request)
Enter fullscreen mode Exit fullscreen mode

Authentication middleware:

def require_auth(request, next_handler):
    if not request.get("user"):
        return "401 Unauthorized"
    return next_handler(request)
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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)