fastapi-contract
Catch FastAPI breaking changes that OpenAPI diff can't see.
v0.1 Technical Preview: the CLI currently checks FAPI001 response projection compatibility only. The public API and snapshot format are not yet stable.
Why
A FastAPI route can keep the same OpenAPI response schema while returning fewer
fields. fastapi-contract compares extracted runtime configuration to a saved
baseline and reports changes in the supported contract surface. It complements
OpenAPI diff tools; a SAFE result is not a guarantee of full API compatibility.
Installation
Requires Python 3.11+. Install into your application's dependency environment.
pip install fastapi-contract
To try it from source, run
pip install . in this repository, or use uv sync --group dev and prefix the
commands below with uv run.
Quickstart
From your application project directory, create app/__init__.py and
app/main.py. Put this in app/main.py:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
email: str
@app.get("/users", response_model=User)
def users():
return {"id": 1, "email": "alice@example.com"}
Save the accepted contract:
fastapi-contract snapshot app.main:app \
-o fastapi-contract-baseline.json
After editing the application, compare it with that file:
fastapi-contract check app.main:app \
--against fastapi-contract-baseline.json
An unchanged app prints SAFE: No incompatibility detected in the supported contract surface. and exits 0. snapshot also accepts --output instead of -o.
No server needs to run.
Example breaking change
Before:
@app.get("/users", response_model=User)
After (replace only the decorator in the example):
@app.get(
"/users",
response_model=User,
response_model_exclude={"email"},
)
The OpenAPI response schema still describes User, but the runtime response
loses email. Running check against the original snapshot prints:
BREAKING
FAPI001 GET /users: response fields removed by projection: email
The command exits 1.
Exit codes
| Code | Result | Meaning |
|---|---|---|
| 0 | SAFE | No incompatibility detected in the supported surface; snapshot saved successfully for snapshot. |
| 1 | BREAKING / REVIEW | A breaking projection change or a relevant change requiring human review. |
| 2 | ERROR | Invalid arguments, app loading/file/analysis failure, invalid snapshot, or incompatible environment. |
Supported rules
FAPI001 — Effective Response Projection Changed is the only enabled rule. Slice 1 supports static top-level projection of supported flat response models. Unsupported relevant changes can produce REVIEW rather than a definite finding. See the FAPI001 specification for the precise boundary.
Current limitations
- Technical Preview, FAPI001 only; no alias, omission, or request-side checks.
- Analysis is limited to the supported top-level Slice 1 surface. It does not exercise endpoints or prove their returned values match their declarations.
- Baselines are snapshot files using the existing schema v1. There is no Git baseline loading, environment reconstruction, or automatic baseline refresh.
- Baseline and current must use the same runtime/dependency environment. The checker requires matching recorded Python, FastAPI, Starlette, and Pydantic versions; keep exact versions consistent, including Python patch versions.
- App loading imports
module:attributein the current process. Module-level side effects execute; only load trusted application code. No subprocess isolation is provided. Run from the project directory or install its modules. - Startup, lifespan, and the server are not run. Routes registered only during startup or lifespan are therefore not included.
CI usage
Commit fastapi-contract-baseline.json alongside your application. For a project
that has fastapi-contract in its locked uv dependencies, a minimal GitHub Actions
job is:
name: Contract check
on: [push, pull_request]
jobs:
contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version-file: .python-version
- run: python -m pip install uv
- run: uv sync --frozen
- run: >-
uv run fastapi-contract check app.main:app
--against fastapi-contract-baseline.json
Commit an exact Python patch version in .python-version and use that version
and the same lockfile when generating the baseline. Keep the committed baseline
unchanged during ordinary CI checks. For an intentional contract change, review
its consumer impact, rerun snapshot in the matching environment, and commit
the updated baseline with the application change for review.
Roadmap
- Response serialized alias compatibility
- Response omission behavior
- Request-side runtime contracts
- Other hidden runtime contracts
- Git baseline workflow
Development
uv sync --group dev
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv build
License
Apache License 2.0. See LICENSE.
Metadata
Release files for fastapi-contract 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_contract-0.1.0.tar.gz | 95.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_contract-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 120.6 kB
Release files / fastapi_contract-0.1.0.tar.gz
| Download URL | fastapi_contract-0.1.0.tar.gz |
|---|---|
| Size | 95.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
732a773ad49bfaa2b563d2a9e7e0142a1f30d7a8bd8fbb0e01814d86e8d34557
|
|
BLAKE2b-256 checksum How to use checksums |
6309e7f55f46d961f7c50bb96a8350604e1bef8da53dc6b7006376ef230c6762
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / fastapi_contract-0.1.0-py3-none-any.whl
| Download URL | fastapi_contract-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b09ff3bf183b603a749c95d2bebc8ac4e3f4d5d2db51ff337a3cdb879b3f0888
|
|
BLAKE2b-256 checksum How to use checksums |
f37f7b74eb88746fb5e557d7cd378baba3833af7ba626a670a3a3e569ae39bf2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log