Skip to content

Quick Start

This walks through defining and using a small client end to end.

1. Define your models

Requests and responses are plain Pydantic models.

from pydantic import BaseModel


class GetUserRequest(BaseModel):
    user_id: str


class CreateUserRequest(BaseModel):
    name: str
    email: str


class DeleteUserRequest(BaseModel):
    user_id: str


class User(BaseModel):
    id: str
    name: str
    email: str

2. Declare the API

Subclass APIClient and annotate each endpoint. The annotation carries everything: the request model, the response model, and the path.

from typing import Literal
from clientcraft import Get, Post, Delete
from clientcraft.client import APIClient


class UserAPI(APIClient):
    get_user: Get[GetUserRequest, User, Literal["/users/{user_id}"]]
    create_user: Post[CreateUserRequest, User, Literal["/users"]]
    delete_user: Delete[DeleteUserRequest, None, Literal["/users/{user_id}"]]

Declare clients at module level

clientcraft resolves the annotations with typing.get_type_hints, so the request/response models must be importable from the client's module scope. Defining everything at module level (not inside a function) keeps this simple.

3. Pick a backend and call

Concrete backends are imported from their submodule (see Backends):

from clientcraft.backends.requests import RequestsBackend

backend = RequestsBackend()
client = UserAPI(base_url="https://api.example.com", backend=backend)

# Fully typed — get_user returns a User
user = client.get_user(GetUserRequest(user_id="123"))
print(user.name)

new_user = client.create_user(
    CreateUserRequest(name="Alice", email="alice@example.com")
)

# None response type -> returns None
client.delete_user(DeleteUserRequest(user_id="123"))

Default headers and timeout

Both are set on the client and applied to every request:

client = UserAPI(
    base_url="https://api.example.com",
    backend=backend,
    default_headers={"Authorization": "Bearer <token>"},
    default_timeout=30.0,
)

Going async

The same shape works with AsyncAPIClient and an async backend — see Async Clients.

from clientcraft import AsyncGet
from clientcraft.async_client import AsyncAPIClient
from clientcraft.backends.aiohttp import AiohttpBackend


class AsyncUserAPI(AsyncAPIClient):
    get_user: AsyncGet[GetUserRequest, User, Literal["/users/{user_id}"]]


async with AiohttpBackend() as backend:
    client = AsyncUserAPI(base_url="https://api.example.com", backend=backend)
    user = await client.get_user(GetUserRequest(user_id="123"))