TypeDB Integration Guide¶
This guide covers TypeDB-specific concepts, driver API, TypeQL syntax, and integration considerations for TypeBridge.
Table of Contents¶
- Server and Driver Compatibility
- Key TypeDB Concepts
- TypeDB ORM Design Considerations
- TypeQL Syntax Requirements
- TypeDB Driver 3.x API
- TypeDB 3.x Syntax and Behavior Changes
Server and Driver Compatibility¶
Support window¶
TypeBridge supports TypeDB servers in the 3.8.x through 3.12.x range. There is no
published 3.9 line (TypeDB skipped it); verified compatibility tags include 3.8.3,
3.10.4, 3.11.5, and 3.12.0, while the current exact release-artifact lane also
targets 3.12.1. Server 3.7.x is protocol-compatible with band 7 (see below) but
falls below the declared floor and is not supported.
Protocol bands¶
TypeDB uses a protocol-band model. A driver natively speaks exactly one band; a server accepts a set of bands, and a connection succeeds only when the server accepts the driver's band. Through 3.11 every server accepted exactly its own band, so cross-band connections always failed. Starting with 3.12 acceptance is asymmetric (measured live): server 3.12 retains backward compatibility with band-8 drivers, while a band-9 (3.12) driver is refused by a 3.11 server at connect.
| Band | Driver versions | Servers accepting it |
|---|---|---|
| 7 | 3.7*, 3.8, 3.10 | 3.7*, 3.8, 3.10 |
| 8 | 3.11 | 3.11, 3.12 |
| 9 | 3.12 | 3.12 |
* 3.7 is protocol-compatible with band 7 but unsupported. The band and acceptance maps
are declared once in crates/core's version module and consumed by every tier.
Multi-band runtime¶
TypeBridge's wheel embeds three TypeDB Rust driver lines — band 7 (3.8.1,
vendored fork), band 8 (3.11.5, upstream), and band 9 (3.12.0, vendored) — and
negotiates the connect band from the server's accepted-band set at connect time.
A confirmed 3.12 server upgrades to band 9 so given rows are available; band 8
remains its safe discovery/fallback path. A single TypeBridge release therefore
serves the full supported window without user-side driver selection.
This release line serves TypeDB 3.8 through 3.12:
| Dimension | Supported range | Notes |
|---|---|---|
| TypeDB server | 3.8.0–3.12.x | Band-7 (3.8.x, 3.10.x), band-8 (3.11.x), and band-9-native (3.12.x) servers; 3.12 retains band 8 for discovery/fallback and dispatch is automatic |
Python typedb-driver |
3.8–3.12 on CPython 3.12–3.13; 3.12.0 on CPython 3.14 | The public extra permits supported lines on 3.12–3.13 so callers can match the server; its 3.14 branch pins the first line with a compatible native wheel. The development extra uses 3.11.5 below 3.14 for the default test server. This installed driver does not control the ORM's embedded 3.8–3.12 runtime |
| CPython interpreter | 3.12–3.14 | Defaulted generic parameters use the compatible typing_extensions surface on 3.12; the abi3 native wheel supports all declared interpreter lines |
Feature gates vs. the version window¶
The support window says which servers TypeBridge connects to; individual
TypeDB features can still require a newer server within that window. Feature
requirements are declared in crates/core's version module (Feature) and
checked client-side against the server version detected at connect time, so
a feature used against a too-old server fails with a versioned TypeBridge
error naming both versions — never a server-side syntax error.
Current feature gates:
| Feature | Minimum server | Gated surfaces |
|---|---|---|
@doc/@meta schema annotations |
3.12.0 | SchemaManager.sync_schema, migration executor steps |
given-stage parameterized queries |
3.12.0 | Database.execute_with_rows, TransactionContext.execute_with_rows; insert_many and typed-query predicate transport use it opportunistically |
When the server version is unknown (band-7 gRPC fallback without a
server_version= pin), gated DDL is sent as-is and the server decides;
given-stage queries are rejected by the runtime when the negotiated driver
band cannot carry input rows. Typed queries do not require the feature: they
automatically retain validated inline literal lowering on older bands and use
one bounded given row only when band-9 transport is active.
The band map itself is {7, 8, 9} in the default build: band 9 is the
vendored TypeDB 3.12 driver, and its protocol is the wire path for given
rows. One measured hazard shapes the connect design: a band-9 connection
attempt crashes a 3.11 server outright, so the gRPC fallback discovers
unknown servers through band 8 and upgrades to band 9 only after the
reported version proves it safe.
Update-safety contract¶
Database.connect() raises a human-readable, actionable error when its embedded
runtime connects to a server outside the supported window (e.g. 3.7.x), before
any transaction is attempted and never mid-operation. The optional installed
Python typedb-driver is not involved in this ORM path. When direct driver
access is requested through Database.driver, its separate gate validates the
installed driver against the server before opening that external connection.
Both errors name the relevant versions without exposing raw protocol numbers.
The direct Python driver follows its own protocol band. On CPython 3.12–3.13 the
development extra selects driver 3.11.5 for the default TypeDB 3.11.5 server.
On CPython 3.14 it selects driver 3.12.0, the first line with a CPython 3.14
native wheel, and direct driver connections must therefore target TypeDB 3.12.
This interpreter-specific restriction does not apply to Database.connect(),
typed queries, or other ORM operations backed by TypeBridge's embedded Rust
runtime.
By default, the gate calls GET :<http_port>/v1/version on the server's HTTP API port.
If that endpoint is unreachable and no exact server version was supplied, TypeBridge
falls back to gRPC protocol negotiation: it tries the band-8 driver first, then the
band-7 driver. If both gRPC attempts fail, the gate fails loudly with all attempted
paths in the error.
HTTP version-probe port¶
TypeDB exposes a version endpoint over HTTP in addition to its gRPC port. TypeBridge
probes this endpoint at connect time to determine the server version before committing
to a driver construction. The probe port defaults to 8000 but must match the HTTP port
of the specific TypeDB instance being targeted unless you supply server_version.
On a host running multiple TypeDB instances (for example, a primary on :8000 and a
test instance remapped to :9000), probing the wrong port silently validates the wrong
server — the gate passes against instance A while the gRPC connection goes to instance B.
Configuring the correct port prevents that mismatch.
Python ORM
# Default port (8000) — no configuration needed for a standard single-instance setup
db = Database(address="localhost:1729", database="mydb")
db.connect()
# Explicit port — required when TypeDB's HTTP port is remapped
db = Database(address="localhost:1729", database="mydb", http_port=9000)
db.connect()
gRPC-only deployments¶
If the TypeDB server exposes gRPC but disables or firewalls the HTTP API, TypeBridge can still connect by falling back to gRPC protocol negotiation. The fallback tries band 8 before band 7 and reports both failures if neither driver can open a connection.
For strict exact-version validation on gRPC-only deployments, pass the exact server version explicitly:
When server_version is set, TypeBridge skips the HTTP probe and gRPC fallback,
validates the supplied semantic version against the same support window, derives the
protocol band from the validated version, and then opens the matching embedded Rust
driver.
Use an exact TypeDB version such as 3.8.3, 3.10.4, 3.11.5, or 3.12.1; do not
substitute a raw protocol band. Band 7 includes unsupported TypeDB 3.7.x as well as
supported 3.8.x and 3.10.x. When HTTP is unavailable, automatic band-7 fallback can
identify the protocol band but not the exact semantic version; use server_version
when that exact validation is required. Invalid or unsupported pinned versions still
fail with VersionError.
Node binding
import { RustDatabase, ensureDatabase } from "@type-bridge/node";
// Pass httpPort in the options object when TypeDB's HTTP port is remapped.
const db = RustDatabase.connect("localhost:1729", "mydb", { httpPort: 9000 });
// Pass serverVersion to skip HTTP probing for gRPC-only deployments.
ensureDatabase("localhost:1729", "mydb", { serverVersion: "3.10.4" });
const grpcOnly = RustDatabase.connect("localhost:1729", "mydb", {
serverVersion: "3.10.4",
});
Server config
[typedb]
address = "localhost:1729"
database = "mydb"
http_port = 9000
# Optional: exact server version for gRPC-only deployments.
server_version = "3.10.4"
The same settings can be supplied with TYPEDB_HTTP_PORT and
TYPEDB_SERVER_VERSION.
Migration CLI
# The _generate command accepts --http-port for the same reason
python -m type_bridge._generate --address localhost:1729 --http-port 9000 ...
Test suite environment variable
Set TYPEDB_HTTP_PORT to override the HTTP probe port across all pytest integration tiers:
The variable is read by tests/utils/typedb_lifecycle.py as TEST_DB_HTTP_PORT and
forwarded to every fixture-level Database construction in tests/integration/conftest.py.
test.sh passes it inline to the Python integration, parity, and Node integration tiers.
Key TypeDB Concepts¶
When implementing features, keep these TypeDB-specific concepts in mind:
1. TypeQL Schema Definition Language¶
TypeDB requires schema definitions before data insertion. The schema defines: - Attribute types: Value types (string, integer, double, etc.) - Entity types: Independent objects that own attributes - Relation types: Connections with explicit role players
2. Role Players¶
Relations in TypeDB are first-class citizens with explicit role players (not just foreign keys).
Example:
relation employment,
relates employee,
relates employer;
person plays employment:employee;
company plays employment:employer;
This is fundamentally different from relational databases where foreign keys create implicit relationships.
3. Attribute Ownership¶
Attributes can be owned by multiple entity/relation types. This enables powerful data modeling:
Both person and company can own the same name attribute type.
4. Inheritance¶
TypeDB supports type hierarchies for entities, relations, and attributes:
entity animal @abstract,
owns name;
entity dog sub animal,
owns breed;
entity cat sub animal,
owns color;
Subtypes inherit all attributes and roles from their parent types.
5. Rule-based Inference¶
TypeDB can derive facts using rules. This is important for query design:
rule transitive-location:
when {
(located: $x, location: $y) isa locating;
(located: $y, location: $z) isa locating;
} then {
(located: $x, location: $z) isa locating;
};
Rules allow queries to match both explicit and inferred data.
TypeDB ORM Design Considerations¶
When implementing ORM features for TypeDB:
1. Mapping Challenge¶
TypeDB's type system is richer than traditional ORMs: - Relations are not simple foreign keys - Attributes are independent types, not columns - Role players create explicit, typed connections
TypeBridge approach:
- Model attributes as Python classes (subclasses of Attribute)
- Model entities/relations as Python classes with TypeFlags
- Use Role[T] for type-safe role player definitions
2. TypeQL Generation¶
The ORM needs to generate valid TypeQL queries from Python API calls.
Example: Insert query generation
# Python API
person = Person(name=Name("Alice"), age=Age(30))
manager.insert(person)
# Generated TypeQL
insert $e isa person,
has name "Alice",
has age 30;
Example: Relation insert with role players
# Python API
employment = Employment(
employee=alice,
employer=techcorp,
position=Position("Engineer")
)
manager.insert(employment)
# Generated TypeQL
match
$employee isa person, has name "Alice";
$employer isa company, has name "TechCorp";
insert
(employee: $employee, employer: $employer) isa employment,
has position "Engineer";
3. Transaction Semantics¶
TypeDB has strict transaction types that must be respected:
- READ: For read-only queries (match, fetch)
- WRITE: For data modification (insert, delete, update)
- SCHEMA: For schema definition (define, undefine)
TypeBridge automatically selects the correct transaction type based on the operation.
4. Schema Evolution¶
Consider how Python model changes map to TypeDB schema updates:
Adding a field:
# Before
class Person(Entity):
name: Name = Flag(Key)
# After (add email)
class Person(Entity):
name: Name = Flag(Key)
email: Email # New field
TypeBridge detects this as an additive change (safe).
Removing a field:
# Before
class Person(Entity):
name: Name = Flag(Key)
age: Age
# After (remove age)
class Person(Entity):
name: Name = Flag(Key)
TypeBridge detects this as a breaking change and raises SchemaConflictError (prevents data loss).
5. Role Handling¶
Relations require explicit role mapping:
class Employment(Relation):
flags = TypeFlags(name="employment")
# Explicit role definitions with types
employee: Role[Person] = Role("employee", Person)
employer: Role[Company] = Role("employer", Company)
# Attributes
position: Position
This generates:
relation employment,
relates employee,
relates employer,
owns position;
person plays employment:employee;
company plays employment:employer;
TypeQL Syntax Requirements¶
When generating TypeQL schema definitions, always use the following correct syntax:
1. Attribute Definitions¶
2. Entity Definitions¶
# ✅ CORRECT
entity person,
owns name @key,
owns age @card(0..1);
# ❌ WRONG
person sub entity,
owns name @key;
3. Entity Inheritance with Abstract¶
# ✅ CORRECT: Abstract entity without parent
entity content @abstract,
owns id @key;
# ✅ CORRECT: Abstract entity with inheritance
entity page @abstract, sub content,
owns page-id,
owns bio;
# ✅ CORRECT: Concrete entity with inheritance
entity person sub page,
owns email;
Note: @abstract comes before sub, separated by comma.
4. Relation Definitions¶
# ✅ CORRECT
relation employment,
relates employee,
relates employer,
owns salary @card(0..1);
# ❌ WRONG
employment sub relation,
relates employee;
5. Relation Inheritance with Abstract¶
# ✅ CORRECT: Abstract relation
relation social-relation @abstract,
relates related @card(2);
# ✅ CORRECT: Concrete relation with inheritance
relation friendship sub social-relation,
relates friend as related @card(2);
6. Cardinality Annotations¶
# ✅ CORRECT: Use .. (double dot) syntax
@card(1..5)
@card(2..) # Unbounded max
@card(0..1)
# ❌ WRONG: Comma syntax
@card(1,5)
7. Key and Unique Annotations¶
@keyimplies@card(1..1), never output both@uniquedoes not imply cardinality; preserve the independently declared@cardwhen a unique ownership is required or multi-valued- Bare TypeQL
@uniqueretains the server default@card(0..1)
# ✅ CORRECT
entity person,
owns email @key; # Implies @card(1..1)
# ❌ WRONG (redundant)
entity person,
owns email @key @card(1..1); # Don't specify both
TypeDB Driver 3.x API¶
The driver API for 3.x differs from earlier versions:
1. No Separate Sessions¶
Transactions are created directly on the driver:
# ✅ TypeDB 3.x
driver.transaction(database_name, TransactionType.READ)
# ❌ Old API (TypeDB 2.x)
session = driver.session(database_name, SessionType.DATA)
transaction = session.transaction(TransactionType.READ)
2. Single Query Method¶
transaction.query(query_string) returns Promise[QueryAnswer]:
# Execute query
promise = transaction.query("match $x isa person; fetch $x;")
# Must call .resolve() to get results
result = promise.resolve()
This works for all query types:
- define (schema definition)
- insert (data insertion)
- match (data querying)
- fetch (data fetching)
- delete (data deletion)
- update (data modification)
3. TransactionType Enum¶
Three transaction types:
- TransactionType.READ: Read-only queries
- TransactionType.WRITE: Data modification
- TransactionType.SCHEMA: Schema definition
from typedb.driver import TransactionType
# Schema transaction
tx = driver.transaction(db_name, TransactionType.SCHEMA)
tx.query("define entity person, owns name;").resolve()
tx.commit()
# Write transaction
tx = driver.transaction(db_name, TransactionType.WRITE)
tx.query('insert $x isa person, has name "Alice";').resolve()
tx.commit()
# Read transaction
tx = driver.transaction(db_name, TransactionType.READ)
result = tx.query("match $x isa person; fetch $x;").resolve()
tx.close() # No commit needed for READ
4. Authentication¶
Requires Credentials(username, password) even for local development:
from type_bridge import Credentials, TypeDB, create_driver_options
# ✅ With credentials (required)
driver = TypeDB.driver(
"localhost:1729",
Credentials("admin", "password"),
create_driver_options(),
)
# Omitting Credentials is invalid in TypeDB 3.x.
TypeDB 3.x Syntax and Behavior Changes¶
TypeDB 3.x introduced important syntax and behavior changes that affect query generation:
Query Syntax Changes¶
1. Type Queries Use isa Instead of sub¶
TypeBridge implementation:
- All generated queries use isa for type matching
- sub is only used in schema definitions for inheritance
2. Cannot Query Root Types Directly¶
Cannot match on entity, relation, or attribute root types:
# ❌ This will fail in TypeDB 3.x
match $x isa entity;
# ✅ Query specific entity types
match $x isa person;
TypeBridge implementation: - Never generates queries for root types - Always queries specific entity/relation types
3. Pagination Requires Explicit Sorting¶
offset relies on consistent sort order:
# ✅ CORRECT: Explicit sorting for pagination
match $p isa person;
sort $p asc;
offset 10;
limit 5;
# ⚠️ UNPREDICTABLE: No sort order
match $p isa person;
offset 10;
limit 5;
TypeBridge implementation:
- Always includes sort clause when using offset
- Default sort order: ascending by entity variable
4. Clause Ordering Matters¶
offset must come before limit:
# ✅ CORRECT order
match $p isa person;
sort $p asc;
offset 10;
limit 5;
# ❌ WRONG order (syntax error)
match $p isa person;
limit 5;
offset 10;
TypeBridge implementation:
- Query builder enforces correct clause order
- Clause order: match → sort → offset → limit
Implementation Considerations¶
When generating TypeQL queries:
- Use
isafor type matching in all queries - Avoid querying root types (
entity,relation,attribute) - Always include explicit
sortclause when usingoffsetfor pagination - Ensure clause order:
match→sort→offset→limit
Migration from TypeDB 2.x¶
If migrating from TypeDB 2.x:
Schema changes: - No changes needed (schema syntax is compatible)
Query changes:
- Replace $x sub person with $x isa person
- Add sort clause when using offset
- Ensure correct clause ordering
Driver changes:
- Install type-bridge[typedb-driver] and select the driver line matching the
target TypeDB server. On CPython 3.14, use driver and server 3.12.
- Remove session management code
- Add credentials for authentication
- Use transaction.query() instead of separate query methods
Example: Complete TypeDB 3.x Query¶
from type_bridge import Credentials, TransactionType, TypeDB, create_driver_options
# Connect with credentials
driver = TypeDB.driver(
"localhost:1729",
Credentials("admin", "password"),
create_driver_options(),
)
# Create/use database
if not driver.databases.contains("mydb"):
driver.databases.create("mydb")
# Query with proper syntax
tx = driver.transaction("mydb", TransactionType.READ)
# TypeDB 3.x query: isa, sort, offset, limit
query = """
match
$p isa person, has name $name;
sort $name asc;
offset 10;
limit 5;
fetch
$p: name;
"""
result = tx.query(query).resolve()
tx.close()
TypeDB 3.x Resources¶
For abstract types and interface hierarchies, see abstract-types.md.
For internal implementation details, see internals.md.
For API reference, see the User Guide.