Detect breaking changes between API schemas (OpenAPI / JSON Schema)
Project description
api-schema-diff
Detect breaking changes between API schemas (OpenAPI / JSON Schema) in a deterministic, CI-friendly way.
api-schema-diff is a CLI tool designed to answer one question reliably:
"Will this schema change break existing clients?"
Why this exists
API changes often break clients silently.
Common causes:
- Removing endpoints
- Removing fields
- Changing parameter requirements
- Changing request/response schemas
api-schema-diff catches these issues before they reach production.
Features
Supported inputs
- OpenAPI 3.x (JSON / YAML)
- JSON Schema (generic structure diff)
Breaking changes detected
- Removed paths or operations
- Removed query / path / header parameters
- Parameters becoming required
- Parameter schema type changes
- Request body removed
- Request body becoming required
- Request body schema breaking changes
- Removed response status codes
- Response schema breaking changes
- Removed properties in schemas
- Property type changes
- Optional โ required fields
Non-breaking changes detected
- Added paths or operations
- Added optional parameters
- Added optional request bodies
- Added response status codes
- Added optional properties
Designed for CI
- Deterministic output
- Stable exit codes
- JSON output for automation
- Works offline (no cloud / no LLM dependency)
Installation
pip install api-schema-diff
Or install from source:
git clone https://github.com/teolzr/schema-diff.git
cd schema-diff
pip install -e .
Requirements:
- Python 3.10 or higher
typer>=0.12rich>=13.7
Docker
Run without installing Python:
# Pull the image
docker pull ghcr.io/teolzr/schema-diff:latest
# Run with local files
docker run --rm -v $(pwd):/workspace ghcr.io/teolzr/schema-diff:latest old.yaml new.yaml
# Check version
docker run --rm ghcr.io/teolzr/schema-diff:latest --version
# Use in CI/CD
docker run --rm \
-v $(pwd):/workspace \
ghcr.io/teolzr/schema-diff:latest \
schemas/v1.yaml schemas/v2.yaml \
--format json
Available tags:
latest- Latest stable releasev0.1.4- Specific versionmain- Latest from main branch
๐ Full Docker documentation โ
Usage
Basic usage
api-schema-diff old.json new.json
Exit codes:
0โ No breaking changes found1โ Breaking changes detected
Output formats
Text output (default):
api-schema-diff old.json new.json
Output:
BREAKING CHANGES FOUND
โโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโณโโโโโโโโโโณโโโโโโโโโโณโโโโโโโโโโโโโโโโโโโโโโโโ
โ Type โ Path โ Old Typeโ New Typeโ Message โ
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
โ removed_fieldโ User.email โ โ โ โ
โ type_change โ Order.amountโ <number>โ <string>โ โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโ
JSON output:
api-schema-diff old.json new.json --format json
Output:
{
"breaking": [
{
"type": "removed_field",
"severity": "breaking",
"path": "User.email",
"old_type": null,
"new_type": null,
"message": null
},
{
"type": "type_change",
"severity": "breaking",
"path": "Order.amount",
"old_type": "<number>",
"new_type": "<string>",
"message": null
}
],
"non_breaking": []
}
CLI Options
api-schema-diff [OPTIONS] OLD_FILE NEW_FILE
Arguments:
OLD_FILE- Path to the old schema file (JSON or YAML)NEW_FILE- Path to the new schema file (JSON or YAML)
Options:
--format [text|json]- Output format (default:text)--fail-on-breaking / --no-fail-on-breaking- Exit with code 1 when breaking changes are found (default:true)--help- Show help message
Report-only mode
Use --no-fail-on-breaking to always exit with code 0 (useful for reporting without failing CI):
api-schema-diff old.json new.json --no-fail-on-breaking
Examples
Example 1: Generic JSON diff
old.json:
{
"User": {
"email": "a@b.com",
"age": 30
},
"Order": {
"amount": 12.5
}
}
new.json:
{
"User": {
"age": "30"
},
"Order": {
"amount": "12.5"
},
"NewThing": {}
}
api-schema-diff old.json new.json
Detected changes:
- Breaking:
User.emailfield removed - Breaking:
User.agetype changed from number to string - Breaking:
Order.amounttype changed from number to string - Non-breaking:
NewThingobject added
Example 2: OpenAPI diff
old-api.yaml:
openapi: 3.0.0
paths:
/users:
get:
parameters:
- name: limit
in: query
required: false
schema:
type: integer
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
name:
type: string
email:
type: string
new-api.yaml:
openapi: 3.0.0
paths:
/users:
get:
parameters:
- name: limit
in: query
required: true # Now required!
schema:
type: integer
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
name:
type: string
# email removed!
api-schema-diff old-api.yaml new-api.yaml
Detected changes:
- Breaking: Query parameter
limitbecame required - Breaking: Response property
emailremoved
CI/CD Integration
GitHub Actions
name: Schema Diff Check
on:
pull_request:
paths:
- 'api/schema.yaml'
jobs:
schema-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install api-schema-diff
run: pip install api-schema-diff
- name: Get old schema from main branch
run: git show origin/main:api/schema.yaml > old-schema.yaml
- name: Check for breaking changes
run: api-schema-diff old-schema.yaml api/schema.yaml
GitLab CI
api-schema-diff:
image: python:3.10
before_script:
- pip install api-schema-diff
script:
- git show origin/main:api/schema.yaml > old-schema.yaml
- api-schema-diff old-schema.yaml api/schema.yaml
only:
changes:
- api/schema.yaml
Pre-commit hook
Create .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: api-schema-diff
name: Check API schema for breaking changes
entry: bash -c 'git show HEAD:api/schema.yaml > /tmp/old-schema.yaml && api-schema-diff /tmp/old-schema.yaml api/schema.yaml'
language: system
files: 'api/schema.yaml'
pass_filenames: false
Development
Setup
# Clone the repository
git clone https://github.com/teolzr/schema-diff.git
cd schema-diff
# Install in development mode
pip install -e .
# Install development dependencies
pip install pytest pytest-cov black ruff
Running tests
# Run all tests
pytest
# Run with coverage
pytest --cov=schema_diff --cov-report=html
# Run specific test file
pytest tests/test_openapi_diff.py
Note: The Python package is still named schema_diff internally, but the PyPI package and CLI command are api-schema-diff.
Code formatting
# Format code
black .
# Lint code
ruff check .
Project structure
schema-diff/
โโโ schema_diff/
โ โโโ __init__.py
โ โโโ cli.py # CLI entry point
โ โโโ diff.py # Generic JSON diff logic
โ โโโ loader.py # Schema file loading
โ โโโ models.py # Data models
โ โโโ rules.py # Breaking change rules
โ โโโ openapi/
โ โโโ __init__.py
โ โโโ diff.py # OpenAPI-specific diff
โ โโโ json_schema_diff.py # JSON Schema diffing
โ โโโ normalizer.py # Schema normalization
โ โโโ resolver.py # $ref resolution
โโโ tests/
โโโ pyproject.toml
โโโ README.md
How it works
- Schema loading: Automatically detects schema type (OpenAPI vs generic JSON/YAML)
- Normalization: Resolves
$refreferences and normalizes structure - Diffing: Compares schemas using rule-based detection
- Classification: Categorizes changes as breaking or non-breaking
- Reporting: Outputs results in human-readable or JSON format
Change detection logic
Breaking changes:
- Removing existing fields/paths โ clients expect them
- Changing types โ clients may send wrong data type
- Making optional fields required โ clients may not send them
- Removing response fields โ clients may depend on them
Non-breaking changes:
- Adding new fields/paths โ clients can ignore them
- Making required fields optional โ clients can still send them
- Adding new optional fields โ backwards compatible
Roadmap
- Support for OpenAPI 2.0 (Swagger)
- GraphQL schema diffing
- Custom rule configuration
- HTML report generation
- API compatibility scoring
- Severity levels (error, warning, info)
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Run tests and linting
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see LICENSE file for details
Credits
Built with:
Related Projects
- openapi-diff - OpenAPI comparison tool
- swagger-diff - Swagger API comparison
- json-schema-diff - JSON Schema comparison
Support
- ๐ Issues: GitHub Issues
- ๐ฌ Discussions: GitHub Discussions
Note: Repository name is schema-diff, but PyPI package name is api-schema-diff
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file api_schema_diff-0.1.5.tar.gz.
File metadata
- Download URL: api_schema_diff-0.1.5.tar.gz
- Upload date:
- Size: 17.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d39b3a96ffd42171829e714297ab17b6ea9b89bc76f6ca26070c0849ece0dcb1
|
|
| MD5 |
fbd5ebf530e4d8511962e8b270278ae5
|
|
| BLAKE2b-256 |
bd55bb300643474ee671b57fd8d7321a2e179527c9e122ea19541d17d8c50251
|
Provenance
The following attestation bundles were made for api_schema_diff-0.1.5.tar.gz:
Publisher:
release.yml on teolzr/schema-diff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
api_schema_diff-0.1.5.tar.gz -
Subject digest:
d39b3a96ffd42171829e714297ab17b6ea9b89bc76f6ca26070c0849ece0dcb1 - Sigstore transparency entry: 767343295
- Sigstore integration time:
-
Permalink:
teolzr/schema-diff@2389f2ebe906a782bbef4bc6ab59d07fe97aa8bc -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/teolzr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2389f2ebe906a782bbef4bc6ab59d07fe97aa8bc -
Trigger Event:
push
-
Statement type:
File details
Details for the file api_schema_diff-0.1.5-py3-none-any.whl.
File metadata
- Download URL: api_schema_diff-0.1.5-py3-none-any.whl
- Upload date:
- Size: 17.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfee710ec732604e6ce081dd9c05a423b12e0753c577547f467e3909768da304
|
|
| MD5 |
61e548b0bdef0be04b0dd44b2674a89b
|
|
| BLAKE2b-256 |
0f088555796cbb817d484c79be9c087ecc2d22886329dcc50303673f5c7132ee
|
Provenance
The following attestation bundles were made for api_schema_diff-0.1.5-py3-none-any.whl:
Publisher:
release.yml on teolzr/schema-diff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
api_schema_diff-0.1.5-py3-none-any.whl -
Subject digest:
bfee710ec732604e6ce081dd9c05a423b12e0753c577547f467e3909768da304 - Sigstore transparency entry: 767343309
- Sigstore integration time:
-
Permalink:
teolzr/schema-diff@2389f2ebe906a782bbef4bc6ab59d07fe97aa8bc -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/teolzr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2389f2ebe906a782bbef4bc6ab59d07fe97aa8bc -
Trigger Event:
push
-
Statement type: