Skip to content

Contracts

A contract is the source of truth for one HTTP operation. It is immutable data, separate from the use case that implements the operation.

Declare an operation

from pydantic import BaseModel, Field
from tenchi.contracts import contract


class CreateTodo(BaseModel):
    title: str = Field(min_length=1, max_length=120)


class Todo(BaseModel):
    id: str
    title: str
    completed: bool


class CreatedTodoHeaders(BaseModel):
    location: str = Field(alias="Location")


create_todo_contract = contract(
    method="POST",
    path="/todos",
    request=CreateTodo,
    response=Todo,
    response_headers=CreatedTodoHeaders,
    status=201,
    name="create_todo",
    summary="Create a todo",
    tags=("todos",),
)

Pydantic's TypeAdapter validates every declared boundary type. You may use Pydantic models, dataclasses, standard collections, unions, and other types Pydantic supports; inputs are not restricted to BaseModel subclasses.

Input sources

Contracts name four validated input sources:

OptionSourceUse-case parameter
request=Request bodyrequest
params=Path parametersparams
query=Query stringquery
headers=Request headersheaders
class GetTodoParams(BaseModel):
    todo_id: str


class RequestHeaders(BaseModel):
    api_version: str = Field(alias="X-API-Version")


get_todo_contract = contract(
    method="GET",
    path="/todos/{todo_id}",
    params=GetTodoParams,
    headers=RequestHeaders,
    response=Todo,
)

Field aliases are the wire names for path, query, and header fields and appear the same way in OpenAPI. route() verifies that path placeholders match the declared parameter schema.

Media types

JSON is the default. Declare a different body or response representation with request_media_type= and response_media_type=. Text declarations validate a string; other non-JSON media types validate bytes or strings. Requests with a missing or mismatched Content-Type fail with a framework-owned 415 before decoding.

Charset-qualified text types are encoded and decoded strictly. An unsupported declared charset fails when the contract is built rather than during a call.

Metadata and visibility

summary, description, and tags feed OpenAPI. deprecated accepts True or a timestamp; sunset records the removal date.

public defaults to False. Authentication hooks can use public=True to exempt an operation, and OpenAPI uses the same value when security schemes are configured. The metadata does not authenticate or authorize a request by itself.

webhook=True marks a signed inbound operation. Unlike descriptive metadata, it is enforced: create_app() requires a matching exact-body verifier binding. See Receive signed webhooks.

One access-control signal

Authentication hooks should exempt operations through info.contract.public, not URL paths or documentation tags. OpenAPI uses the same metadata to remove global security from public operations.

Runtime limits

Invalid combinations and unsupported schemas fail when contract() is called. Continue with routes and server, responses, and errors.