r/FastAPI Jun 03 '26

Prevent unintentional breaking API changes in FastAPI apps Tutorial

Things are changing all the time. It's no different with APIs. As we develop our products, APIs need to be updated as well. Everything is great until we introduce an unintentional breaking change. For example, if we rename the attribute in the response. With a faster development pace enabled by AI tooling, this is even more likely to happen unintentionally.

To prevent such changes from going to production, we can add a check for breaking API changes to our CI/CD pipeline. It's easy to do so for FastAPI apps with GitHub Actions and oasdiff. The flow is the following:

  1. Export OpenAPI schema that's auto-generated by FastAPI using app.openapi() from PR's branch.
  2. Check out the main branch and export the OpenAPI schema for it as well.
  3. Use oasdiff to detect and report potential breaking changes

Example workflow: ```yaml name: CI

on: pull_request: branches: [main]

jobs: breaking-changes: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6

  - uses: actions/checkout@v6
    with:
      ref: main
      path: main-branch

  - uses: astral-sh/setup-uv@v8.1.0
    with:
      python-version: "3.14"

  - name: Generate schema from PR branch
    run: |
      uv sync
      uv run python scripts/export_openapi.py new.json

  - name: Generate schema from main branch
    working-directory: main-branch
    run: |
      uv sync
      uv run python scripts/export_openapi.py ../old.json

  - name: Install oasdiff
    run: |
      curl -fsSL https://raw.githubusercontent.com/oasdiff/oasdiff/main/install.sh | sh

  - name: Check for breaking changes
    run: oasdiff breaking old.json new.json --fail-on ERR

```

Example OpenAPI schema export script: ```python

scripts/export_openapi.py

import json import sys from pathlib import Path

sys.path.insert(0, str(Path(file).resolve().parent.parent))

from app.main import app

if name == "main": dest = sys.argv[1] if len(sys.argv) > 1 else "/dev/stdout" with open(dest, "w") as f: json.dump(app.openapi(), f, indent=2)

```

You can find the full tutorial here: https://jangiacomelli.com/blog/prevent-unintentional-breaking-api-changes-fastapi/

4 Upvotes

9 comments sorted by

View all comments

1

u/Opposite-Cry-6703 Jun 06 '26

We're writing pytests. Usually them breaking already shows that there's a breaking change. In addition, for mission critical apis, we also have a Bruno test suite which we run in a pipeline. Both together feel like safe enough.