Cadwyn: Production-Ready FastAPI API Versioning

6.2.0 · active · verified Mon Apr 06

Cadwyn is a Python library that provides production-ready, Stripe-like API versioning for FastAPI applications. It allows developers to maintain only the latest version of their API implementation, automatically generating older versions and handling backward compatibility. This approach encapsulates version changes in independent modules, simplifying business logic. The current version is 6.2.0, with an active development and release cadence.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to set up a basic FastAPI application with Cadwyn for API versioning. It defines two versions (2024-01-01 and 2023-01-01), with a version change making the `email` field optional in `UserCreateRequest` for the older version. The `Cadwyn` instance handles the generation and inclusion of versioned routers, allowing your core business logic to interact only with the latest schema.

import datetime
from fastapi import FastAPI
from pydantic import BaseModel, Field
from cadwyn import Cadwyn, VersionBundle, VersionedAPIRouter
from cadwyn.structure import VersionChange, schema

# 1. Define your schemas in the 'latest' version
class UserCreateRequest(BaseModel):
    name: str
    email: str

class UserResource(BaseModel):
    id: int
    name: str
    email: str

# 2. Define your API versions
# Use ISO date strings for versions for future compatibility (v6.x+)
VERSION_2024_01_01 = datetime.date(2024, 1, 1)
VERSION_2023_01_01 = datetime.date(2023, 1, 1)

# 3. Define version changes
class ChangeEmailFieldToOptional(VersionChange):
    description = 'Make email field optional in UserCreateRequest'
    version = VERSION_2023_01_01

    @schema(UserCreateRequest).alter
    def alter_user_create_request(cls):
        cls.email = Field(default=None, union_of=[cls.email, type(None)])

# 4. Create a VersionBundle
version_bundle = VersionBundle(
    latest_version=VERSION_2024_01_01,
    old_versions=[VERSION_2023_01_01],
    version_changes=[ChangeEmailFieldToOptional],
)

# 5. Initialize Cadwyn and the versioned router
app = FastAPI()
cadwyn_app = Cadwyn(
    versions=version_bundle,
    api_version_header_name='x-api-version',
    latest_schemas_package=__name__,
    old_versions_package=__name__
)

# Use a standard FastAPI router but include it via Cadwyn
router = VersionedAPIRouter(version_bundle=version_bundle)

@router.post('/users', response_model=UserResource)
async def create_user(user: UserCreateRequest):
    # Business logic always works with the latest schema
    return {"id": 1, "name": user.name, "email": user.email}

@router.get('/users/{user_id}', response_model=UserResource)
async def get_user(user_id: int):
    return {"id": user_id, "name": "John Doe", "email": "john.doe@example.com"}

# Cadwyn generates and includes versioned routes
cadwyn_app.generate_and_include_versioned_routers(app, router)

# To run: uvicorn your_module_name:app --reload
# Test with curl -H 'x-api-version: 2024-01-01' -X POST -H 'Content-Type: application/json' -d '{"name": "Alice", "email": "alice@example.com"}' http://localhost:8000/users
# Test with curl -H 'x-api-version: 2023-01-01' -X POST -H 'Content-Type: application/json' -d '{"name": "Bob"}' http://localhost:8000/users

view raw JSON →