FastAPI API contracts: response filtering and dependency tests
Test FastAPI's public response contracts with local examples: reject invalid input, detect direct-response bypasses, isolate dependency overrides and check when yield cleanup runs.
What the local experiment actually checks
We used FastAPI 0.142.2, Pydantic 2.13.5, Starlette 1.7.0 and AnyIO 4.15.1 on Windows with Python 3.12.4. The main run used HTTPX2 2.13.1; a separate HTTPX 0.28.1 comparison passed the same observations. Starlette's current documentation recommends HTTPX2 and treats plain HTTPX as deprecated. The comparison is evidence, not an installation recommendation.
The fixture executes ten Python blocks from the pinned official skill without rewriting their source. It supplies a synthetic session/query to the database-shaped example and captures the function-scope example's print call as a lifecycle event. Separately identified BB Skills examples demonstrate strict input contracts, invalid returned data and a raw-response counterexample. Each run passed 29 named observations across 27 in-process HTTP requests.
This is a TestClient experiment, not a running public server. External socket connections are prohibited; Windows event-loop loopback sockets are allowed and counted. There are no real credentials, database connections, cloud accounts or paid model calls. An AI client did not execute the complete skill. Frontend serving, telemetry export, streaming transport, deployment, authorization and concurrent persistence need separate tests.
Match the skill to the installed library
FastAPI ships its official agent skill inside the Python package. The downloaded resource is a fixed snapshot of release 0.142.2 at commit 78c4324f0c56bc5282a459978779a9e532d19709. All seven skill files match the official wheel byte for byte, and the complete repository MIT notice is retained. The package contains a separately marked BB Skills preface and original upstream-original/SKILL.md.
A current website or the repository's moving master branch may describe a newer feature than your project installs. Inspect the project lock and the skill bundled with that version before applying its advice. Keep the snapshot's reference files together; installing a single copied instruction file loses part of its context. This directory does not automatically update the fixed download when upstream changes.
Start with the official FastAPI skill and local evidence. For complementary model-level work, see Pydantic API Model Contracts. The partial-update guide explains why omitted values, explicit null and validation after merging have different responsibilities.
Separate bad input from a broken response
Checking only that an endpoint returns 200 leaves important behavior untested. The upstream examples let us verify path bounds, a query length limit, a required query value, positive body values and an annotated list. For rejected input, the fixture checks both the status and the error location/type. A rejection elsewhere in the request should not accidentally count as the expected result.
| Controlled input or return | Observed result | Useful assertion |
|---|---|---|
| Path ID zero where the lower bound is one | 422 client input error | The path field and greater-than-or-equal error match. |
| Required project query omitted | 422 client input error | The missing value is in the query, not the body. |
| Zero price where price must be positive | 422 client input error | The body price has the expected bound failure. |
| Unknown field in the independent strict request model | 422 client input error | The unknown field reports extra-forbidden. |
| Endpoint returns data missing a required public response value | ResponseValidationError; 500 when server exceptions are disabled | The failure belongs to the server's returned response. |
TestClient normally raises server exceptions, which helps diagnose broken application code. To inspect the HTTP 500 response, create a separate client with raise_server_exceptions=False. Do not change a failed response-validation test to expect 422: that would conflate the endpoint's invalid return with a caller's invalid request.
from fastapi.testclient import TestClient
with TestClient(app, raise_server_exceptions=False) as client:
response = client.get("/broken")
assert response.status_code == 500
Test the serialized public response, including bypasses
The upstream response-model example returns an internal object containing a synthetic secret field. Its public Item model exposes only name and description. We verified both the actual JSON response and the public OpenAPI schema. Checking documentation alone would not establish what the caller receives.
The independent example deliberately returns an extra internal note from a normal dictionary. A public response model removes that note. A second, explicitly marked counterexample returns a JSONResponse object directly under the same declared response model. The internal note remains in that raw response. This is a framework behavior to account for, not a newly discovered vulnerability in a deployed service.
| Mechanism | What this fixture observed | What it does not provide |
|---|---|---|
| Public response model with a normal value | Only declared public fields appear. | Identity verification or ownership checks. |
| Direct JSONResponse object | The controlled extra field remains; model filtering is bypassed. | Automatic enforcement of the declared response schema. |
| Strict input model with extra=forbid | An unknown request field is rejected. | A policy for which user may change an allowed field. |
| Synthetic dependency override | The local provider runs in a request. | Real OAuth, token validation or production permissions. |
Assert on the serialized body that matters to the client. For a public product contract, exact key membership catches accidental fields without relying only on a status code:
response = client.post("/products", json={"name": "Fixture", "cost": 1})
assert response.status_code == 200
assert response.json() == {"name": "Fixture", "cost": 1.0}
The raw-response route is an intentional teaching counterexample. Do not deploy the fixture as an application. Review custom response construction, exception handlers, logs and exports independently. Use Evidence-Based Security Code Review to plan a wider review; this small experiment does not certify an API.
Observe and clean up dependency overrides
The original dependency example returns a constant Hello World response even though a synthetic user dependency runs. A successful response therefore cannot prove which provider executed. Our test override records its invocation, and the response still matches the original constant. After clearing the override, another request does not call the overriding provider again.
Key an override by the original function object. Keep cleanup in a finally block so a failed assertion cannot leave test-only behavior in later requests:
calls = []
def local_identity():
calls.append("fixture-alice")
return {"username": "fixture-alice"}
app.dependency_overrides[get_current_user] = local_identity
try:
response = client.get("/items/")
assert response.json() == {"message": "Hello World"}
assert calls == ["fixture-alice"]
finally:
app.dependency_overrides.clear()
That cleanup is appropriate here because the isolated application starts with no overrides. In a larger suite that intentionally shares other overrides, restore the mapping you owned rather than clearing someone else's setup. Keep test applications independent and verify the request after restoration. The Test Driven Development resource can help turn a concrete failure into a useful regression.
Verify yield cleanup against ASGI events
For the native default request-scope dependency, our synthetic session releases exactly once after the response body event. For the native function-scope example, the finally block runs before the response-start event. A thin observation wrapper records ASGI events; the original generators remain unchanged.
This verifies event order for these local, non-streaming examples. It does not establish TCP delivery, streaming disconnect cleanup, database transactions or behavior behind your production proxy. Choose a dependency's lifetime from the resources the endpoint actually needs. Add error-path and streaming tests before assuming a release order demonstrated by a small successful response applies to a long-lived stream.
Reproduce in a clean, free local environment
Download the resource, inspect its files and work from the extracted fastapi directory. The archive contains instructions, references, fixtures and evidence; it contains no Python runtime, wheel or installed browser/identity state. Dependency installation contacts the public package registry separately. The fixture itself makes only in-process API requests and permits only event-loop loopback socket connections.
python -m venv .venv
# Windows:
.venv\Scripts\python -m pip install -r examples\requirements.txt
.venv\Scripts\python examples\run_contract_fixture.py
# macOS/Linux use .venv/bin/python and forward-slash paths.
The requirement file pins FastAPI 0.142.2, Pydantic 2.13.5, Starlette 1.7.0, AnyIO 4.15.1 and HTTPX2 2.13.1. The historical HTTPX 0.28.1 run is retained separately. Reproduction writes a new environment and timestamp to examples/native-api-evidence.json; read the named cases rather than requiring the JSON file to have the old byte hash.
The script has a bounded diagnostic timeout. A restricted Windows execution sandbox can block the event loop's local socketpair even without an external request; test that primitive or use an environment allowing local communication. Do not switch to a production endpoint to bypass an environment problem. For project environments and locks, see Modern Python Project Tooling.
Sources, attribution and the next tests
Primary sources: version-pinned official FastAPI skill, complete MIT notice and upstream copyright, FastAPI's bundled-skill explanation, response models, dependency override testing, yield dependency lifetime and Starlette TestClient behavior and HTTPX2 guidance.
BB Skills authored this guide with AI assistance and checked its claims against the named local observations and primary documentation. The original skill is attributed to the FastAPI project and Sebastián Ramírez; independent BB Skills examples are labeled separately. There is no upstream endorsement or certification.
Extend the experiment for your actual application: authenticated principals, ownership and roles, persistence and rollback, streaming cancellation, custom responses, observability and deployment. These local records are a starting point for that work, not a substitute for it.