Middleware

Middleware is a light “plugin” system for altering driver’s request/response/exception processing.

This page will walk you through the concept of middleware in the apiwrappers library.

Writing your own middleware

A middleware factory is a callable that takes a callable and returns a middleware. A middleware is a callable that takes same argument as Driver.fetch() and returns a response.

The most simple way is to write a middleware as function:

from apiwrappers.structures import NoValue


def simple_middleware(handler):

    def middleware(request, timeout=NoValue()):
        # Code to be executed before request is made
        response = handler(request, timeout)
        # Code to be executed after request is made
        return response

    return middleware

Since middleware is used by drivers, and the one we’ve written can be used only by regular driver, we also need to provide an async implementation:

from apiwrappers.structures import NoValue


def simple_async_middleware(handler):

    async def middleware(request, timeout=NoValue()):
        # Code to be executed before request is made
        response = await handler(request, timeout)
        # Code to be executed after request is made
        return response

    return middleware

As you can see, the only difference is that in async middleware we have to await the handler call.

To help us reduce this code duplication apiwrappers provides a BaseMiddleware class. Subclassing one you can then override it hook methods like that:

from typing import NoReturn

from apiwrappers import Request, Response
from apiwrappers.middleware import BaseMiddleware


class SimpleMiddleware(BaseMiddleware):
    def process_request(self, request: Request) -> Request:
        # Code to be executed before request is made
        return request

    def process_response(self, response: Response) -> Response:
        # Code to be executed after request is made
        return response

    def process_exception(
        self, request: Request, exception: Exception
    ) -> NoReturn:
        # Code to be executed when any exception is raised
        raise exception

Using middleware

Middleware are used by drivers and each driver accepts a list of middleware.

Although, middleware we defined earlier literally does nothing, it still can be used like that:

>>> from apiwrappers import make_driver
>>> driver = make_driver("requests", SimpleMiddleware)
>>> driver
# RequestsDriver(Authorization, SimpleMiddleware, ...

Note, that even we provide only ``SimpleMiddleware`` the driver also has ``Authorization`` middleware. That’s because some drivers have middleware that should always be present.

You can also change driver middleware after creation by simply reassigning Driver.middleware attribute:

>>> driver.middleware = []
>>> driver
# RequestsDriver(Authorization, ...

The order of the default middleware can be overridden by explicitly specifying it:

>>> driver.middleware = [SimpleMiddleware, Authorization]
>>> driver
# RequestsDriver(SimpleMiddleware, Authorization, ...

Middleware order

The order of middleware matters because a middleware can depend on other middleware.

Before making actual request, middleware are executed in the order they are defined. After getting the response middleware are executed in the reverse order.