unihttp is a modern, fast, and type-safe library for developing API clients in Python. It provides a unified interface for defining API methods and supports multiple HTTP backend clients (HTTPX, Requests, Aiohttp).
It is designed to be:
- Declarative: Define API methods as simple Python classes with type hints, keeping your code clean and readable.
- Type-Safe: Leverages Python's type system and
adaptixfor robust data validation and serialization. - Flexible: Switch between Sync and Async backends or different HTTP libraries without changing your business logic.
- Extensible: Easy middleware system and error handling hooks.
Install using pip:
pip install unihttpTo include a specific HTTP backend (recommended):
pip install "unihttp[httpx]" # For HTTPX (Sync/Async) support
# OR
pip install "unihttp[requests]" # For Requests (Sync) support
# OR
pip install "unihttp[aiohttp]" # For Aiohttp (Async) supportfrom dataclasses import dataclass
from unihttp import BaseMethod, Path, Body
@dataclass
class User:
id: int
name: str
email: str
@dataclass
class GetUser(BaseMethod[User]):
__url__ = "/users/{id}"
__method__ = "GET"
id: Path[int]
@dataclass
class CreateUser(BaseMethod[User]):
__url__ = "/users"
__method__ = "POST"
name: Body[str]
email: Body[str]Create a client class that inherits from one of the unihttp clients (e.g., HTTPXSyncClient or HTTPXAsyncClient) and bind your methods to it using bind_method.
from unihttp import bind_method
from unihttp.clients.httpx import HTTPXSyncClient
from unihttp.serializers.adaptix import AdaptixDumper, AdaptixLoader, DEFAULT_RETORT
class UserClient(HTTPXSyncClient):
def __init__(self):
super().__init__(
base_url="https://jsonplaceholder.typicode.com",
request_dumper=DEFAULT_RETORT,
response_loader=DEFAULT_RETORT
)
get_user = bind_method(GetUser)
create_user = bind_method(CreateUser)
# Initialize the client
client = UserClient()Now you can call your API methods directly on the client instance.
# Get a user
user = client.get_user(id=1)
print(user) # User(id=1, name='...', email='...')
# Create a user
new_user = client.create_user(name="Alice", email="alice@example.com")
print(new_user)You can also use the client without subclassing by calling call_method directly.
client = HTTPXSyncClient(
base_url="https://jsonplaceholder.typicode.com",
request_dumper=AdaptixDumper(DEFAULT_RETORT),
response_loader=AdaptixLoader(DEFAULT_RETORT),
)
user = client.call_method(GetUser(id=1))Simply switch to an Async client. The definition of your methods remains exactly the same!
import asyncio
from unihttp.clients.httpx import HTTPXAsyncClient
async def main():
async with HTTPXAsyncClient(
base_url="https://jsonplaceholder.typicode.com",
request_dumper=AdaptixDumper(DEFAULT_RETORT),
response_loader=AdaptixLoader(DEFAULT_RETORT),
) as client:
user = await client.call_method(GetUser(id=1))
print(user)
asyncio.run(main())You can add middleware to intercept requests and responses. Middleware works for both Sync and Async clients (using Middleware and AsyncMiddleware respectively).
from unihttp.middlewares.base import Middleware
class AuthMiddleware(Middleware):
def __init__(self, token: str):
self.token = token
def handle(self, request, next_handler):
request.header["Authorization"] = f"Bearer {self.token}"
return next_handler(request)
client = HTTPXSyncClient(
# ...
middleware=[AuthMiddleware("my-secret-token")]
)unihttp provides hooks for handling errors at both the Method level and the Client level.
on_error(response): Override in your Method class to handle specific status codes for that endpoint.handle_error(response, method): Override in your Client class to handle global errors (e.g., token expiration).validate_response(response): Override to inspect the response body (e.g., for APIs that return 200 OK but contain an error field).
@dataclass
class GetUser(BaseMethod[User]):
# ...
def on_error(self, response):
if response.status_code == 404:
return None # Return None/Custom object instead of raising
# Return None to let the client handle it or raise default exception
return super().on_error(response)- HTTPX:
unihttp.clients.httpx.HTTPXSyncClient,unihttp.clients.httpx.HTTPXAsyncClient - Requests:
unihttp.clients.requests.RequestsSyncClient - Aiohttp:
unihttp.clients.aiohttp.AiohttpClient
MIT