Skip to content

Fastapi

FastAPI provider example.

This modules defines a simple provider which will be tested with Pact in the provider test. As Pact is a consumer-driven framework, the consumer defines the contract which the provider must then satisfy.

The provider is the application which receives requests from another service (the consumer) and returns a response. In this example, we have a simple endpoint which returns a user's information from a (fake) database.

This also showcases how Pact tests differ from merely testing adherence to an OpenAPI specification. The Pact tests are more concerned with the practical use of the API, rather than the formally defined specification. The User class defined here has additional fields which are not used by the consumer. Should the provider later decide to add or remove fields, Pact's consumer-driven testing will provide feedback on whether the consumer is compatible with the provider's changes.

Note that the code in this module is agnostic of Pact. The pact-python dependency only appears in the tests. This is because the consumer is not concerned with Pact, only the tests are.

Attributes

FAKE_DB: dict[int, User] = {} module-attribute

app = FastAPI() module-attribute

logger = logging.getLogger(__name__) module-attribute

Classes

User

Bases: BaseModel

User data class.

Attributes

admin: bool instance-attribute
created_on: Annotated[datetime, PlainSerializer(lambda dt: dt.strftime('%Y-%m-%dT%H:%M:%S%z'), return_type=str, when_used='json')] instance-attribute
email: Optional[str] instance-attribute
hobbies: list[str] instance-attribute
id: int instance-attribute
ip_address: Optional[str] instance-attribute
name: str instance-attribute

Functions

create_new_user(user: dict[str, Any]) -> User async

Create a new user .

PARAMETER DESCRIPTION
user

The user data to create

TYPE: dict[str, Any]

RETURNS DESCRIPTION
User

The status code 200 and user data if successfully created, HTTP 404 if not

Source code in examples/src/fastapi.py
@app.post("/users/")
async def create_new_user(user: dict[str, Any]) -> User:
    """
    Create a new user .

    Args:
        user: The user data to create

    Returns:
        The status code 200 and user data if successfully created, HTTP 404 if not
    """
    if "id" in user:
        raise HTTPException(status_code=400, detail="ID should not be provided.")
    uid = len(FAKE_DB)
    FAKE_DB[uid] = User(
        id=uid,
        name=user["name"],
        created_on=datetime.now(tz=timezone.utc),
        email=user.get("email"),
        ip_address=user.get("ip_address"),
        hobbies=user.get("hobbies", []),
        admin=user.get("admin", False),
    )
    return FAKE_DB[uid]

delete_user(uid: int) async

Delete an existing user .

PARAMETER DESCRIPTION
uid

The ID of the user to delete

TYPE: int

RETURNS DESCRIPTION

The status code 204, HTTP 404 if not

Source code in examples/src/fastapi.py
@app.delete("/users/{uid}", status_code=204)
async def delete_user(uid: int):  # noqa: ANN201
    """
     Delete an existing user .

    Args:
        uid: The ID of the user to delete

    Returns:
        The status code 204, HTTP 404 if not
    """
    if uid not in FAKE_DB:
        raise HTTPException(status_code=404, detail="User not found")

    del FAKE_DB[uid]

get_user_by_id(uid: int) -> User async

Fetch a user by their ID.

PARAMETER DESCRIPTION
uid

The ID of the user to fetch

TYPE: int

RETURNS DESCRIPTION
User

The user data if found, HTTP 404 if not

Source code in examples/src/fastapi.py
@app.get("/users/{uid}")
async def get_user_by_id(uid: int) -> User:
    """
    Fetch a user by their ID.

    Args:
        uid: The ID of the user to fetch

    Returns:
        The user data if found, HTTP 404 if not
    """
    user = FAKE_DB.get(uid)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user