terminusdb-migrations
Schema diff and migration planning for TerminusDB, using Pydantic models as the desired schema.
terminusdb-migrations compares the live TerminusDB schema with schema documents generated from Python models and produces a migration plan that can be reviewed, dry-run, and applied through the native TerminusDB Migration API.
Pydantic models
↓
desired TerminusDB schema
↓
diff ← current TerminusDB schema
↓
migration plan
↓
native TerminusDB Migration API
It is designed to work with terminusdb-pydantic and terminusdb-async.
Status: early-stage / pre-1.0. Inspect generated migration plans before applying them to production data.
Project status: this is a community package and is not an official TerminusDB migration tool.
Installation
With uv:
uv add terminusdb-migrations
With pip:
python -m pip install terminusdb-migrations
Python 3.11+ is supported.
Internal package dependencies intentionally stay broad across patch releases:
terminusdb-async >=0.1.0,<1.0.0
terminusdb-pydantic >=0.1.0,<1.0.0
The minimum compatible version is only raised when a release actually requires newer API.
Quick start
Define the desired schema as Pydantic models:
from terminusdb_pydantic import LexicalKey, TerminusModel
class Discipline(TerminusModel):
__terminus_key__ = LexicalKey("code")
code: str
name: str
description: str | None = None
MODELS = [Discipline]
Build a migration plan against the live TerminusDB schema:
from terminusdb_async import AsyncTerminusClient
from terminusdb_migrations import MigrationManager
async def inspect_schema() -> None:
async with AsyncTerminusClient(
"http://localhost:6363",
organization="admin",
database="edtech",
username="admin",
password="root",
) as db:
manager = MigrationManager(db, MODELS)
plan = await manager.plan()
print(plan.render())
Apply the reviewed plan:
await manager.apply(
plan,
author="schema-bot",
message="Synchronize schema",
)
Dry run
Use TerminusDB's migration dry-run before applying generated operations:
await manager.apply(
plan,
author="schema-bot",
message="Check schema synchronization",
dry_run=True,
)
If a plan is marked destructive, allow_destructive=True is still required before it can be sent to TerminusDB. Destructive plans must be acknowledged explicitly.
What the planner does
The current planner is intentionally conservative.
| Schema difference | Planner behavior |
|---|---|
| New class | CreateClass |
| New enum | schema document insert |
| New optional property | CreateClassProperty |
| New required property | warning; no default is invented |
| Existing property type changed | CastClassProperty |
| Enum value added | ExpandEnum |
| Enum value removed | warning + destructive |
| Key changed | ChangeKey + destructive |
| Property removed from model | warning + destructive |
| Class or enum absent from models | warning + destructive |
This table describes the current 0.1.x behavior.
MigrationPlan
A generated plan exposes:
plan.operations
plan.schema_documents
plan.warnings
plan.destructive
plan.empty
For human review:
print(plan.render())
operations
Native TerminusDB migration operations ready for the Migration API.
schema_documents
Schema documents that need to be inserted outside the migration operation list. In the current implementation, a newly introduced enum is handled this way.
warnings
Changes that the planner refuses to guess automatically.
destructive
Signals that applying the plan may remove information or materially change identity semantics.
Safety model
Automatic schema diff cannot always infer developer intent.
For example, these two schemas are ambiguous:
class Discipline(TerminusModel):
title: str
and:
class Discipline(TerminusModel):
name: str
The difference may mean either:
- rename
titletoname; or - delete
titleand add an unrelatedname.
The planner therefore prefers warnings over silently destructive guesses.
The same principle applies to required properties without defaults, enum contractions, removals, and key changes.
CLI
The package installs the tdb-migrate command.
Expose a model registry from a Python module:
# app/models.py
from terminusdb_pydantic import LexicalKey, TerminusModel
class Discipline(TerminusModel):
__terminus_key__ = LexicalKey("code")
code: str
name: str
MODELS = [Discipline]
Inspect changes:
tdb-migrate plan \
--models app.models:MODELS \
--db edtech
Dry-run the migration:
tdb-migrate apply \
--models app.models:MODELS \
--db edtech \
--author schema-bot \
--message "Synchronize schema" \
--dry-run
Apply after review:
tdb-migrate apply \
--models app.models:MODELS \
--db edtech \
--author schema-bot \
--message "Synchronize schema"
For a plan explicitly marked destructive:
tdb-migrate apply \
--models app.models:MODELS \
--db edtech \
--author schema-bot \
--message "Apply reviewed destructive migration" \
--allow-destructive
Configuration
The CLI accepts command-line arguments and environment variables for the TerminusDB connection.
Useful environment variables:
TERMINUSDB_URL
TERMINUSDB_ORG
TERMINUSDB_USER
TERMINUSDB_PASSWORD
TERMINUSDB_TOKEN
Example:
export TERMINUSDB_URL=http://localhost:6363
export TERMINUSDB_ORG=admin
export TERMINUSDB_USER=admin
export TERMINUSDB_PASSWORD=root
tdb-migrate plan --models app.models:MODELS --db edtech
End-to-end example
from terminusdb_async import AsyncTerminusClient
from terminusdb_migrations import MigrationManager
from terminusdb_pydantic import LexicalKey, TerminusModel
class Discipline(TerminusModel):
__terminus_key__ = LexicalKey("code")
code: str
name: str
description: str | None = None
async def migrate() -> None:
async with AsyncTerminusClient(
"http://localhost:6363",
organization="admin",
database="edtech",
username="admin",
password="root",
) as db:
manager = MigrationManager(db, [Discipline])
plan = await manager.plan()
if plan.warnings:
for warning in plan.warnings:
print("WARNING:", warning)
print(plan.render())
if not plan.empty:
await manager.apply(
plan,
author="schema-bot",
message="Synchronize schema",
dry_run=True,
allow_destructive=plan.destructive,
)
Current boundaries
The package is not intended to replace TerminusDB's native migration/versioning mechanisms. It only generates and applies plans on top of them.
In 0.1.x:
- rename intent is not automatically inferred from schema state alone;
- required-property data backfills are not invented;
- destructive removals are not silently executed;
- schema diff behavior is deliberately small and explicit.
Package family
| Package | Purpose |
|---|---|
terminusdb-async |
Async TerminusDB HTTP client |
terminusdb-pydantic |
Pydantic v2 ↔ JSON-LD and schema generation |
terminusdb-migrations |
Schema diff and migration planning |
Development
The project uses uv:
uv sync --group dev
uv run pytest -q
uv build --no-sources
CI tests Python 3.11, 3.12, and 3.13.
Development follows GitHub Flow on GitLab: short-lived branches are merged into main through Merge Requests. See CONTRIBUTING.md.
Versioning
The package is pre-1.0. Internal package compatibility is expressed with ranges such as:
"terminusdb-async>=0.1.0,<1.0.0"
"terminusdb-pydantic>=0.1.0,<1.0.0"
Patch releases do not raise these lower bounds unless they actually depend on new API.
Metadata
Release files for terminusdb-migrations 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| terminusdb_migrations-0.1.1.tar.gz | 9.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| terminusdb_migrations-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.4 kB
Release files / terminusdb_migrations-0.1.1.tar.gz
| Download URL | terminusdb_migrations-0.1.1.tar.gz |
|---|---|
| Size | 9.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d58ccbed701cd3348f2b158d59446bc3b4debf56b8e659a13e6404f07095223a
|
|
BLAKE2b-256 checksum How to use checksums |
5b5b49f0a690054baddbec5593f948dc349d0f3f663d53c545d4b035bf8b9a33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / terminusdb_migrations-0.1.1-py3-none-any.whl
| Download URL | terminusdb_migrations-0.1.1-py3-none-any.whl |
|---|---|
| Size | 7.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c0db4c20d8b0b88eb5b0e601bc7a7397d5d19c9c65859bc3cefecb7f52fef9f
|
|
BLAKE2b-256 checksum How to use checksums |
7444f8f7cceed4c8025996e59362a2fb8994853622b9b73c52facb29484c237d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|