Best Practices
This guide covers essential best practices for building robust, performant applications with TypeDB drivers. Following these practices will help you avoid common pitfalls and create maintainable database code.
Connection management
Resource cleanup
Always ensure connections are properly closed to avoid resource leaks:
Recommended: Automatic management
Use language-specific automatic resource management features:
-
Python
-
Java
-
Rust
with TypeDB.driver(address, credentials, options) as driver:
# Connection automatically closed when exiting block
with driver.transaction(DB_NAME, TransactionType.READ) as tx:
# Transaction automatically closed
results = tx.query("match $x isa entity;")
try (Driver driver = TypeDB.driver(address, credentials, options);
Transaction tx = driver.transaction(dbName, TransactionType.READ)) {
// Connection and transaction automatically closed
QueryAnswer results = tx.query("match $x isa entity;");
}
let driver = TypeDBDriver::new(address, credentials, options).await?;
let tx = driver.transaction(DB_NAME, TransactionType::Read).await?;
let results = tx.query("match $x isa entity;").await?;
// Driver and transaction automatically closed when going out of scope
Manual management
If you need manual resource management, always use try/finally blocks:
-
Python
-
Java
# MANUAL: Explicit cleanup required
driver = TypeDB.driver(address, credentials, options)
try:
tx = driver.transaction(DB_NAME, TransactionType.READ)
try:
results = tx.query("match $x isa entity;")
# Process results...
finally:
tx.close() # Always close transaction
finally:
driver.close() # Always close driver
// MANUAL: Explicit cleanup required
Driver driver = TypeDB.driver(address, credentials, options);
try {
Transaction tx = driver.transaction(dbName, TransactionType.READ);
try {
QueryAnswer results = tx.query("match $x isa entity;");
// Process results...
} finally {
tx.close(); // Always close transaction
}
} finally {
driver.close(); // Always close driver
}
Connection reuse
Connections are expensive to create and should be reused across multiple operations:
# GOOD: Reuse a single connection for multiple operations
with TypeDB.driver(address, credentials, options) as driver:
# Multiple database operations with same connection
for db_name in ["users", "products", "orders"]:
with driver.transaction(db_name, TransactionType.READ) as tx:
results = tx.query("match $x isa entity;")
# Process results...
# AVOID: Creating new connections for each operation
for db_name in ["users", "products", "orders"]:
with TypeDB.driver(address, credentials, options) as driver: # Inefficient!
with driver.transaction(db_name, TransactionType.READ) as tx:
results = tx.query("match $x isa entity;")
Connection pooling and concurrency
TypeDB drivers handle connection pooling internally:
-
Single driver instance: Can handle multiple concurrent operations
-
Thread safety: Drivers are designed for concurrent use across threads
-
Automatic pooling: No need to manage connection pools manually
class TypeDBClient:
def __init__(self, address, credentials, options):
self.driver = TypeDB.driver(address, credentials, options)
def get_users(self):
with self.driver.transaction("users", TransactionType.READ) as tx:
return list(tx.query("match $u isa user; fetch {{ $u.*; }};").resolve().as_concept_documents())
def get_products(self):
with self.driver.transaction("products", TransactionType.READ) as tx:
return list(tx.query("match $p isa product; fetch {{ $p.* }};").resolve().as_concept_documents())
def close(self):
self.driver.close()
Error handling
Server-side errors
Server-side errors are propagated to the driver. Server errors use fixed Error Codes, such as "QEX14", for different types of errors. You can use these to handle specific errors in your application.
-
Python
-
Java
-
Rust
try:
with driver.transaction(DB_NAME, TransactionType.READ) as tx:
result = tx.query("match $x isa person;").resolve()
# Process result...
except TypeDBDriverException as e:
# check for query execution error
if "QEX" in str(e).upper():
print(f"Query execution error: {e}")
# check for TypeQL parsing error
elif "TQL" in str(e).upper():
print(f"TypeQL parsing error: {e}")
else:
print(f"Server error: {e}")
try (Transaction tx = driver.transaction(dbName, TransactionType.READ)) {
QueryAnswer result = tx.query("match $x isa person;").resolve();
// Process result...
} catch (TypeDBDriverException e) {
// check for query execution error
if (e.getMessage().toUpperCase().contains("QEX")) {
System.err.println("Query execution error: " + e.getMessage());
// check for TypeQL parsing error
} else if (e.getMessage().toUpperCase().contains("TQL")) {
System.err.println("TypeQL parsing error: " + e.getMessage());
} else {
System.err.println("Server error: " + e.getMessage());
}
}
let tx = driver.transaction(DB_NAME, TransactionType::Read).await?;
match tx.query("match $x isa person;").await {
Ok(result) => {
// Process result...
},
Err(e) => {
let error_msg = e.to_string().to_uppercase();
// check for query execution error
if error_msg.contains("QEX") {
eprintln!("Query execution error: {}", e);
// check for TypeQL parsing error
} else if error_msg.contains("TQL") {
eprintln!("TypeQL parsing error: {}", e);
} else {
eprintln!("Server error: {}", e);
}
}
}
Performance optimization
To optimize data loading, a common pattern is to asynchronously submit a batch of queries, resolve them all to catch any errors, and then commit:
def batch_load(driver, queries):
with driver.transaction(DB_NAME, TransactionType.WRITE) as tx:
promises = []
for query in queries:
promises.append(tx.query(query))
# resolve all the promises
for p in promises:
p.resolve() # may throw an exception, such as a syntax error
tx.commit()
If you know that all your queries are valid, the resolve()
call can be omitted for even higher performance.
Summary
Following these best practices will help you build robust TypeDB applications:
-
Use automatic resource management whenever possible
-
Reuse connections and handle errors gracefully
-
Choose appropriate transaction types and keep transactions short-lived
-
Batch operations for better performance
-
Secure your credentials and use encryption in production