| name | type-bridge |
| description | Use the type-bridge Python ORM for TypeDB. Covers defining entities, relations, attributes, CRUD operations, queries, expressions, and schema management. Use when working with TypeDB in Python projects. |
type-bridge Python ORM for TypeDB
type-bridge is a Pythonic ORM for TypeDB that provides type-safe abstractions over TypeQL.
Quick Start
from type_bridge import (
Entity, Relation, Role, String, Integer, Double, Boolean,
Flag, Key, Unique, Card, TypeFlags, Database, SchemaManager
)
class Name(String):
pass
class Email(String):
pass
class Age(Integer):
pass
class Person(Entity):
flags = TypeFlags(name="person")
name: Name = Flag(Key)
email: Email = Flag(Unique)
age: Age | None = None
db = Database(address="localhost:1729", database="mydb")
with db:
db.create_database()
schema = SchemaManager(db)
schema.register(Person)
schema.sync_schema()
manager = Person.manager(db)
alice = Person(name=Name("Alice"), email=Email("alice@example.com"), age=Age(30))
manager.insert(alice)
Defining Models
Attribute Types
Attributes are independent types that can be owned by entities and relations.
from type_bridge import String, Integer, Double, Boolean, DateTime, Date, Duration, Decimal
class Name(String):
pass
class Score(Double):
pass
class IsActive(Boolean):
pass
class CreatedAt(DateTime):
pass
from type_bridge import AttributeFlags
class PersonEmail(String):
flags = AttributeFlags(name="email")
Entities
from type_bridge import Entity, Flag, Key, Unique, Card, TypeFlags
class Person(Entity):
flags = TypeFlags(name="person")
person_id: PersonId = Flag(Key)
email: Email = Flag(Unique)
name: Name
age: Age | None = None
tags: list[Tag] = Flag(Card(min=0))
class Artifact(Entity):
flags = TypeFlags(name="artifact", abstract=True)
name: Name
class Document(Artifact):
flags = TypeFlags(name="document")
content: Content
Relations
from type_bridge import Relation, Role, TypeFlags
class Employment(Relation):
flags = TypeFlags(name="employment")
employee: Role[Person] = Role("employee", Person)
employer: Role[Company] = Role("employer", Company)
start_date: StartDate
end_date: EndDate | None = None
class Person(Entity):
flags = TypeFlags(name="person")
name: Name = Flag(Key)
class Company(Entity):
flags = TypeFlags(name="company")
name: Name = Flag(Key)
Relates-side Role Cardinality
For relations where the same role has multiple players of the same type:
from type_bridge import Relation, Role, Card, TypeFlags
class IsSimilarTo(Relation):
flags = TypeFlags(name="is_similar_to")
similar_memory: Role[Memory] = Role(
"similar_memory",
Memory,
cardinality=Card(2, 2),
)
Card variations for roles:
| Python | TypeQL | Meaning |
|---|
Card(2, 2) | @card(2..2) | Exactly 2 players |
Card(1, 3) | @card(1..3) | Between 1 and 3 players |
Card(2) | @card(2..) | At least 2 players (no upper bound) |
Creating instances with multiple role players:
memory1 = Memory(content=Content("First memory"))
memory2 = Memory(content=Content("Second memory"))
similarity = IsSimilarTo(similar_memory=[memory1, memory2])
manager.insert(similarity)
CRUD Operations
Entity Manager
manager = Person.manager(db)
alice = Person(name=Name("Alice"), email=Email("alice@example.com"))
manager.insert(alice)
all_persons = manager.all()
adults = manager.filter(age=Age(18)).all()
seniors = manager.filter(Person.age.gte(Age(65))).all()
first = manager.filter(name=Name("Alice")).first()
total = manager.filter().count()
alice.age = Age(31)
manager.update(alice)
manager.delete(alice)
manager.put(Person(name=Name("Bob"), email=Email("bob@example.com")))
Relation Manager
emp_manager = Employment.manager(db)
alice = Person(name=Name("Alice"))
acme = Company(name=Name("Acme"))
employment = Employment(
employee=alice,
employer=acme,
start_date=StartDate(date(2024, 1, 15))
)
emp_manager.insert(employment)
acme_employees = emp_manager.filter(employer=acme).all()
Transactions
with db.transaction("write") as tx:
person_mgr = Person.manager(tx)
company_mgr = Company.manager(tx)
alice = Person(name=Name("Alice"))
acme = Company(name=Name("Acme"))
person_mgr.insert(alice)
company_mgr.insert(acme)
Query Expressions
Comparison Expressions
Person.age.eq(Age(30))
Person.age.neq(Age(30))
Person.age.gt(Age(18))
Person.age.gte(Age(18))
Person.age.lt(Age(65))
Person.age.lte(Age(65))
Person.name.contains("Ali")
Person.name.like("^A.*")
manager.filter(Person.age.gte(Age(18))).filter(Person.age.lt(Age(65))).all()
Boolean Expressions
from type_bridge.expressions import BooleanExpr
manager.filter(
BooleanExpr.or_(
Person.name.eq(Name("Alice")),
Person.name.eq(Name("Bob"))
)
).all()
manager.filter(
BooleanExpr.not_(Person.status.eq(Status("inactive")))
).all()
Aggregations
result = manager.filter().aggregate(Person.age.avg())
avg_age = result["avg_age"]
result = manager.filter().aggregate(
Person.age.avg(),
Person.salary.sum(),
Person.score.max()
)
Group By
result = manager.group_by(Person.department).aggregate(
Person.salary.avg(),
Person.age.avg()
)
result = manager.group_by(Person.department, Person.level).aggregate(
Person.salary.avg()
)
Pagination
page = manager.filter().limit(10).offset(20).all()
top_5 = manager.filter(Person.score.gte(Score(90))).limit(5).all()
Schema Management
from type_bridge import SchemaManager
schema = SchemaManager(db)
schema.register(Person, Company, Employment)
schema.sync_schema()
schema.sync_schema(force=True)
current = schema.get_schema()
from type_bridge import SchemaDiff
diff = SchemaDiff.compare(old_schema, new_schema)
Built-in Functions (TypeDB 3.8+)
from type_bridge.expressions import iid, label
expr = iid("$e")
expr = label("$t")
Common Patterns
Get by IID
person = manager.get_by_iid("0x1e00000000000000000123")
Polymorphic Queries
class Animal(Entity):
flags = TypeFlags(name="animal", abstract=True)
class Dog(Animal):
flags = TypeFlags(name="dog")
class Cat(Animal):
flags = TypeFlags(name="cat")
animal_manager = Animal.manager(db)
all_animals = animal_manager.all()
Polymorphic Role Players
When a relation role uses an abstract type, queried role players are resolved to their concrete types:
class Profile(Entity):
flags = TypeFlags(name="profile", abstract=True)
profile_id: ProfileId = Flag(Key)
class Person(Profile):
flags = TypeFlags(name="person")
email: Email | None = None
class Organization(Profile):
flags = TypeFlags(name="org")
website: Website | None = None
class Authorship(Relation):
flags = TypeFlags(name="authorship")
author: Role[Profile] = Role("author", Profile)
post: Role[Post] = Role("post", Post)
authorships = Authorship.manager(db).all()
for auth in authorships:
if isinstance(auth.author, Person):
print(f"Person: {auth.author.email}")
elif isinstance(auth.author, Organization):
print(f"Org: {auth.author.website}")
Serialization
person_dict = person.to_dict()
person = Person.from_dict(person_dict)
Raw Queries
results = db.execute_query("""
match $p isa person, has name $n;
fetch { "name": $n };
""", "read")
Cardinality Flags
from type_bridge import Flag, Key, Unique, Card
class Person(Entity):
id: PersonId = Flag(Key)
email: Email = Flag(Unique)
nickname: Nickname | None = Flag(Card(max=1))
phone: Phone = Flag(Card(min=1))
tags: list[Tag] = Flag(Card(min=0))
references: list[Reference] = Flag(Card(min=2, max=5))
Important Notes
-
Keyword-only arguments: All Entity/Relation constructors require keyword arguments
Person(name=Name("Alice"), age=Age(30))
Person(Name("Alice"), Age(30))
-
Attribute instances: Always wrap values in attribute types
person.age = Age(31)
person.age = 31
-
TypeFlags required: Entities and Relations need flags = TypeFlags(name="...")
-
Connection management: Use context managers or explicit connect/close
with Database(...) as db:
-
Schema sync before data: Always sync schema before inserting data
-
Role player matching: Relation CRUD operations identify role players using:
- IID (preferred): If the entity has
_iid set (from being fetched from DB), uses fast IID matching
- Key attributes (fallback): If no IID, uses
Flag(Key) attributes to identify the entity
- Error: If neither is available, raises
ValueError with clear guidance
alice = person_manager.filter(name=Name("Alice")).first()
emp = Employment(employee=alice, employer=company)
emp_manager.insert(emp)
alice = Person(name=Name("Alice"))
emp = Employment(employee=alice, employer=company)
emp_manager.insert(emp)
-
Transaction types:
"read": For queries (no commit needed)
"write": For insert/update/delete (auto-commits)
"schema": For schema changes (auto-commits)
Code Generator
Generate Python models from TypeDB schema files instead of writing them manually.
CLI Usage
python -m type_bridge.generator schema.tql -o ./myapp/models/
python -m type_bridge.generator schema.tql -o ./myapp/models/ --dto
python -m type_bridge.generator schema.tql -o ./myapp/models/ --dto --dto-config myapp.config:dto_config
Programmatic Usage
from type_bridge.generator import generate_models
generate_models("schema.tql", "./myapp/models/")
generate_models("schema.tql", "./myapp/models/", generate_dto=True)
from type_bridge.generator import DTOConfig, BaseClassConfig
config = DTOConfig(
exclude_entities=["internal_counter"],
entity_union_name="GraphNode",
)
generate_models("schema.tql", "./myapp/models/", generate_dto=True, dto_config=config)
Generated Files
myapp/models/
├── __init__.py # Package exports
├── attributes.py # Attribute classes
├── entities.py # Entity classes
├── relations.py # Relation classes
├── registry.py # Schema metadata
├── api_dto.py # Pydantic DTOs (if --dto)
└── schema.tql # Copy of schema
API DTOs (Pydantic)
Generate Pydantic models for REST APIs from your TypeDB schema.
Generated Structure
class PersonOut(BaseDTOOut): ...
class PersonCreate(BaseDTOCreate): ...
class PersonPatch(BaseDTOPatch): ...
class FriendshipOut(BaseRelationOut): ...
class FriendshipCreate(BaseRelationCreate): ...
EntityOut = Annotated[Union[PersonOut, ...], Field(discriminator="type")]
DTOConfig Options
from type_bridge.generator import (
DTOConfig, BaseClassConfig, ValidatorConfig, FieldSyncConfig,
FieldOverride, EntityFieldOverride,
)
config = DTOConfig(
exclude_entities=["display_id_counter", "schema_status"],
iid_field_name="id",
entity_union_name="GraphNode",
relation_union_name="GraphRelation",
validators=[
ValidatorConfig(name="DisplayId", pattern=r"^[A-Z]{1,5}-\d+$"),
],
base_classes=[
BaseClassConfig(
source_entity="artifact",
base_name="BaseArtifact",
inherited_attrs=["display_id", "name", "description"],
extra_fields={"version": "int | None = None"},
field_syncs=[FieldSyncConfig("description", "content")],
create_field_overrides={
"display_id": FieldOverride(required=False),
},
),
],
entity_field_overrides=[
EntityFieldOverride(entity="task", field="status", variant="create",
required=False, default="'proposed'"),
],
composite_entities=[
CompositeEntityConfig(
name="GraphNode",
include_entities=["task", "epic"],
skip_variants={"out", "create", "patch"},
extra_fields_out={"id": "str"},
),
],
preamble="...",
relation_preamble="...",
skip_relation_output=True,
relation_create_base_class="BaseRelationCreate",
)
Usage in FastAPI
from myapp.models.api_dto import PersonOut, PersonCreate, EntityOut
@app.post("/persons", response_model=PersonOut)
def create_person(data: PersonCreate) -> PersonOut:
person = Person(name=Name(data.name))
manager.insert(person)
return PersonOut(iid=person.iid, name=data.name, type="person")
@app.get("/entities/{id}", response_model=EntityOut)
def get_entity(id: str) -> EntityOut:
...