Skip to content

Advanced Usage

Authenticate a Request

Use the AuthMiddleware to provide required authentication to the outgoing request.

Httperactor has a concept of a SubRequest, which represents a request type used by the module that connects to the HTTP server. Httperactor uses the httpx package as the default HTTP client, so the SubRequest becomes the httpx.Request.

Following example defines an AuthMiddleware that adds an Authorization header to the httpx.Request:

import httpx

import httperactor


class BearerTokenAuthMiddleware(httperactor.AuthMiddleware[httpx.Request]):
    def __init__(self, token: str):
        self._token: str = token

    def apply(self, request: httpx.Request) -> httpx.Request:
        request.headers["Authorization"] = f"Bearer {self._token}"
        return request

To have the interactor use the middleware, override the auth property:

import httpx

import httperactor

...

class GetBooksInteractor(httperactor.HttpInteractor[httpx.Request, Sequence[Book], State]):
    @property
    def request(self) -> httperactor.Request[Sequence[Book]]:
        return GetBooksRequest()

    @property
    def auth(self) -> httperactor.AuthMiddleware[httpx.Request] | None:
        return BearerTokenAuthMiddleware(token="MY_SECRET_TOKEN")

Use a custom HttpClient

Httperactor ships with a HttpClient that uses the httpx package for sending the HTTP requests.

You can provide your own client by subclassing the HttpClientBase.

Following example defines a simple client using the tornado package:

from typing import TypeVar

import tornado.httpclient as tornado

from httperactor import AuthMiddleware, HttpClientBase, Request

TResponse = TypeVar("TResponse")


class TornadoHttpClient(HttpClientBase[tornado.HTTPRequest]):
    async def send(
        self,
        request: Request[TResponse],
        auth: AuthMiddleware[tornado.HTTPRequest] | None = None,
    ) -> TResponse | None:
        tornado_request = tornado.HTTPRequest(
            url=f"http://localhost:5000{request.path}",
            method=request.method,
        )
        http_client = tornado.AsyncHTTPClient()

        response = await http_client.fetch(tornado_request)

        return request.map_response(response.body.decode())