Skip to main content

Add versioning and migrations for your Django Ninja API.

Project description

django-ninja-crane

django-ninja-crane

Stripe-style API versioning and migrations for Django Ninja.

django-ninja-crane enables you to:

  • Track API schema changes over time via migration files
  • Automatically transform requests/responses between API versions
  • Serve older API versions to clients while running only your latest code

For more information, check the documentation

Installation

pip install django-ninja-crane

Requirements:

  • Python 3.12+
  • Django 6.0+
  • Django Ninja 1.5.1+

Quick Start

1. Create a Versioned API

Replace any NinjaAPI instances you want to version with VersionedNinjaAPI:

# urls.py
from crane import VersionedNinjaAPI
from myapp.api import router

api = VersionedNinjaAPI(api_label="default", app_label="myapp")
api.add_router("/persons", router)

urlpatterns = [
    path("api/", api.urls),
]

2. Add the Middleware

# settings.py
INSTALLED_APPS = [
    # ...
    "crane",
]

MIDDLEWARE = [
    # ...
    "crane.middleware.VersionedAPIMiddleware",
]

3. Save Your Initial API State

Create the first migration to capture your current API as version 1:

python manage.py makeapimigrations myapp --name "initial"

This creates myapp/api_migrations/default/m_0001_initial.py which records your API's starting state.

4. Make Changes to Your API

Now modify your API schemas. For example, add a phone field to PersonOut:

class PersonOut(Schema):
    id: int
    name: str
    email: str
    phone: str | None = None  # New field

5. Create a Migration for the Changes

Generate a migration that captures the difference between your saved state and the current API:

python manage.py makeapimigrations myapp --name "add_phone_field"

This generates myapp/api_migrations/default/m_0002_add_phone_field.py containing:

  • Schema changes (added/removed/modified fields)
  • Operation changes (new endpoints, modified parameters)

6. Implement Data Transformers

The generated migration includes skeleton transformer functions. For simple cases (adding optional fields, removing fields), the generator creates working implementations automatically:

# myapp/api_migrations/default/m_0002_add_phone_field.py (generated)
def downgrade_person_out(data: dict) -> dict:
    """2 -> 1: Transform person_out for older clients."""
    data.pop("phone", None)
    return data


def upgrade_person_in(data: dict) -> dict:
    """1 -> 2: Transform person_in from older clients."""
    data.setdefault("phone", None)
    return data

For changes that require manual intervention (e.g., adding required fields, breaking schema changes), the generator creates functions that raise NotImplementedError:

def upgrade_user(data: dict) -> dict:
    """1 -> 2: Transform user from older clients."""
    raise NotImplementedError("Provide default value for new field: email")
    # data.setdefault("email", <default_value>)

Replace these with actual implementations before deploying. The validateapimigrations command can check for any remaining NotImplementedError calls.

Now clients requesting X-API-Version: 1 will receive responses without the phone field, while your code only handles the latest version.

Development

# Install dependencies
uv sync

# Run tests
uv run pytest

# Lint & format
uv run ruff check .
uv run ruff format .

# Run dev server
uv run python manage.py runserver

Current functionality

The Current functionality covers most common api migration changes:

  • adding/removing new schemas
  • modifying schema contents
  • modifying operation input pramaters

Limitations

  • union fields (AnyOf), discriminated unions are covered by running all transformers of a union when any of the types in the union are updated.
  • If you remove an endpoint, you need to manually set up a path rewrite pointing it to the new operation to use for that request.

What's next?

  • Proper support for discriminated unions
  • Management command to remove an old version
  • Easier interface for "bring your own version resolver", allowing you to e.g., determine the version from the request's user.
  • Validation to check whether your defined data migrations cover the schema changes
  • Interactive makeapimigrations, prompting whether you want operation or schema migrations for schema changes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_ninja_crane-0.1.5.tar.gz (37.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_ninja_crane-0.1.5-py3-none-any.whl (47.1 kB view details)

Uploaded Python 3

File details

Details for the file django_ninja_crane-0.1.5.tar.gz.

File metadata

  • Download URL: django_ninja_crane-0.1.5.tar.gz
  • Upload date:
  • Size: 37.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for django_ninja_crane-0.1.5.tar.gz
Algorithm Hash digest
SHA256 3f25ef94a2c7f38b7343d30bdda7487cc5e3e1161a20acf8ef38a0842f224de7
MD5 88162dd080a73225cf0f9bac2d3a8927
BLAKE2b-256 5ab2c5273170ab9e9b269a32cd00feda8401a6d6df51c148ee7ee73429dd2155

See more details on using hashes here.

File details

Details for the file django_ninja_crane-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: django_ninja_crane-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 47.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for django_ninja_crane-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 34862d964c94bab2c8cd91a34e88020eca4fef48a81b3c0cab1317f5b63936ec
MD5 c60af6b5767434961d9c1aa344856ecc
BLAKE2b-256 9b659e2ed3e9cf6aa2b00ada43ca676b28643d3d84f3e625a8715ac31b900322

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page