Skip to content

RESTful Mixin

restful

Classes

BearerAuth

BearerAuth(token: str)

Bases: Auth

HTTP Bearer token authentication handler for httpx.

Implements the httpx.Auth interface to automatically add Bearer token authentication to requests.

Parameters:

Name Type Description Default
token str

The bearer token to use for authentication.

required
Source code in audex/lib/restful.py
def __init__(self, token: str):
    self.token = token
Functions
auth_flow
auth_flow(request: Request) -> Generator[Request, Response, None]

Add Bearer token to request Authorization header.

Parameters:

Name Type Description Default
request Request

The HTTP request to authenticate.

required

Yields:

Type Description
Request

The authenticated request with Authorization header.

Source code in audex/lib/restful.py
def auth_flow(self, request: httpx.Request) -> t.Generator[httpx.Request, httpx.Response, None]:
    """Add Bearer token to request Authorization header.

    Args:
        request: The HTTP request to authenticate.

    Yields:
        The authenticated request with Authorization header.
    """
    request.headers["Authorization"] = f"Bearer {self.token}"
    yield request

RESTfulMixin

RESTfulMixin(base_url: str, *, auth: Auth | None = None, proxy: str | URL | None = None, timeout: float = 10.0, http_client: AsyncClient | None = None, default_headers: dict[str, str] | None = None, default_params: dict[str, Any] | None = None)

Bases: AsyncContextMixin

Abstract base class for RESTful API clients with async support.

Provides a high-level interface for making HTTP requests to RESTful APIs with automatic retry logic, response validation using Pydantic models, and built-in error handling.

Features: - Automatic retry with configurable attempts and wait time - Response validation and parsing using Pydantic models - Support for Bearer token and custom authentication - Proxy support - Configurable timeouts and default headers/params - Async context manager support

Attributes:

Name Type Description
url

The base URL for the API.

http_client

The underlying httpx AsyncClient instance.

Parameters:

Name Type Description Default
base_url str

Base URL for all API requests.

required
auth Auth | None

Optional httpx authentication handler.

None
proxy str | URL | None

Optional proxy URL.

None
timeout float

Request timeout in seconds. Defaults to 10.0.

10.0
http_client AsyncClient | None

Optional pre-configured httpx.AsyncClient. If not provided, a new client will be created.

None
default_headers dict[str, str] | None

Optional default headers for all requests.

None
default_params dict[str, Any] | None

Optional default query parameters for all requests.

None
Example
class MyAPIResponse(pyd.BaseModel):
    id: str
    name: str


class MyAPI(RESTful):
    async def get_item(self, item_id: str) -> MyAPIResponse:
        return await self.request(
            f"/items/{item_id}",
            method="GET",
            cast_to=MyAPIResponse,
        )


# Usage
async with MyAPI(
    "https://api.example.com", auth=BearerAuth("token")
) as api:
    item = await api.get_item("123")
    print(item.name)
Source code in audex/lib/restful.py
def __init__(
    self,
    base_url: str,
    *,
    auth: httpx.Auth | None = None,
    proxy: str | httpx.URL | None = None,
    timeout: float = 10.0,
    http_client: httpx.AsyncClient | None = None,
    default_headers: dict[str, str] | None = None,
    default_params: dict[str, t.Any] | None = None,
):
    self.url = base_url
    self.http_client = http_client or httpx.AsyncClient(
        base_url=base_url,
        auth=auth,
        proxy=proxy,
        timeout=timeout,
        headers=default_headers,
        params=default_params,
    )
Functions
request async
request(endpoint: str, *, method: Literal['GET', 'POST', 'PUT', 'DELETE'] = 'GET', headers: Mapping[str, str] | None = None, params: Mapping[str, Any] | None = None, json: Mapping[str, Any] | None = None, cast_to: type[RespT], validate: bool = True, strict: bool = True, raise_for_status: bool = True, max_retries: int = 3, retry_wait: float = 2.0) -> RespT

Make an HTTP request with automatic retry and response validation.

Sends an HTTP request to the specified endpoint and parses the response into the specified Pydantic model. Automatically retries on failure and validates the response data.

Parameters:

Name Type Description Default
endpoint str

API endpoint path (relative to base_url).

required
method Literal['GET', 'POST', 'PUT', 'DELETE']

HTTP method to use. Defaults to "GET".

'GET'
headers Mapping[str, str] | None

Optional request headers to merge with default headers.

None
params Mapping[str, Any] | None

Optional query parameters to merge with default params.

None
json Mapping[str, Any] | None

Optional JSON body for POST/PUT requests.

None
cast_to type[RespT]

Pydantic model class to parse the response into.

required
validate bool

Whether to validate response data. Defaults to True.

True
strict bool

Whether to use strict validation (reject extra fields). Defaults to True.

True
raise_for_status bool

Whether to raise an exception for HTTP error status codes. Defaults to True.

True
max_retries int

Maximum number of retry attempts. Defaults to 3.

3
retry_wait float

Wait time in seconds between retries. Defaults to 2.0.

2.0

Returns:

Type Description
RespT

Parsed response as an instance of cast_to model.

Raises:

Type Description
HTTPStatusError

If the response status indicates an error after all retries.

ValidationError

If response validation fails.

RetryError

If all retry attempts are exhausted.

Example
class UserResponse(pyd.BaseModel):
    user_id: str
    username: str
    email: str


# GET request
user = await api.request(
    "/users/123", method="GET", cast_to=UserResponse
)

# POST request with retry
new_user = await api.request(
    "/users",
    method="POST",
    json={"username": "john", "email": "john@example.com"},
    cast_to=UserResponse,
    max_retries=5,
    retry_wait=3.0,
)
Source code in audex/lib/restful.py
async def request(
    self,
    endpoint: str,
    *,
    method: t.Literal["GET", "POST", "PUT", "DELETE"] = "GET",
    headers: t.Mapping[str, str] | None = None,
    params: t.Mapping[str, t.Any] | None = None,
    json: t.Mapping[str, t.Any] | None = None,
    cast_to: type[RespT],
    validate: bool = True,
    strict: bool = True,
    raise_for_status: bool = True,
    max_retries: int = 3,
    retry_wait: float = 2.0,
) -> RespT:
    """Make an HTTP request with automatic retry and response
    validation.

    Sends an HTTP request to the specified endpoint and parses the response
    into the specified Pydantic model. Automatically retries on failure and
    validates the response data.

    Args:
        endpoint: API endpoint path (relative to base_url).
        method: HTTP method to use. Defaults to "GET".
        headers: Optional request headers to merge with default headers.
        params: Optional query parameters to merge with default params.
        json: Optional JSON body for POST/PUT requests.
        cast_to: Pydantic model class to parse the response into.
        validate: Whether to validate response data. Defaults to True.
        strict: Whether to use strict validation (reject extra fields).
            Defaults to True.
        raise_for_status: Whether to raise an exception for HTTP error
            status codes. Defaults to True.
        max_retries: Maximum number of retry attempts. Defaults to 3.
        retry_wait: Wait time in seconds between retries. Defaults to 2.0.

    Returns:
        Parsed response as an instance of cast_to model.

    Raises:
        httpx.HTTPStatusError: If the response status indicates an error
            after all retries.
        pyd.ValidationError: If response validation fails.
        tenacity.RetryError: If all retry attempts are exhausted.

    Example:
        ```python
        class UserResponse(pyd.BaseModel):
            user_id: str
            username: str
            email: str


        # GET request
        user = await api.request(
            "/users/123", method="GET", cast_to=UserResponse
        )

        # POST request with retry
        new_user = await api.request(
            "/users",
            method="POST",
            json={"username": "john", "email": "john@example.com"},
            cast_to=UserResponse,
            max_retries=5,
            retry_wait=3.0,
        )
        ```
    """

    @tenacity.retry(
        stop=tenacity.stop_after_attempt(max_retries),
        wait=tenacity.wait_fixed(retry_wait),
        reraise=True,
    )
    async def _do_request(
        method: str,
        endpoint: str,
        headers: t.Mapping[str, str] | None,
        params: t.Mapping[str, t.Any] | None,
        json: t.Mapping[str, t.Any] | None,
    ) -> httpx.Response:
        response = await self.http_client.request(
            method,
            endpoint,
            headers=headers,
            params=params,
            json=json,
        )
        if raise_for_status:
            response.raise_for_status()
        return response

    response = await _do_request(method, endpoint, headers, params, json)
    if validate:
        return cast_to.model_validate(response.json(), strict=strict)
    return cast_to.model_construct(**response.json())
close async
close() -> None

Close the HTTP client and cleanup resources.

Should be called when the API client is no longer needed to properly close all connections. Automatically called when using as a context manager.

Example
api = MyAPI("https://api.example.com")
try:
    await api.get_data()
finally:
    await api.close()

# Or use as context manager
async with MyAPI("https://api.example.com") as api:
    await api.get_data()
Source code in audex/lib/restful.py
async def close(self) -> None:
    """Close the HTTP client and cleanup resources.

    Should be called when the API client is no longer needed to properly
    close all connections. Automatically called when using as a context manager.

    Example:
        ```python
        api = MyAPI("https://api.example.com")
        try:
            await api.get_data()
        finally:
            await api.close()

        # Or use as context manager
        async with MyAPI("https://api.example.com") as api:
            await api.get_data()
        ```
    """
    await self.http_client.aclose()

BaseModel

Bases: BaseModel

Base Pydantic model with common configuration.

Sets common configurations for all derived models, such as allowing arbitrary types and enabling ORM mode.

options: show_root_heading: true show_source: true heading_level: 2 members_order: source show_signature_annotations: true separate_signature: true