# 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)Connection management

### [](#_resource_cleanup)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
    

```python
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;")
```

```java
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;");
}
```

```rust
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
    

```python
# 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
```

```java
// 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)Connection reuse

Connections are expensive to create and should be reused across multiple operations:

```python
# 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...
```

```python
# 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)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
    

```python
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)Error handling

### [](#_server_side_errors)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
    

```python
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}")
```

```java
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());
    }
}
```

```rust
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)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:

```python
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()
```

For data loading workloads, we recommend batching approximately 100-1000 queries per transaction. Smaller batches add per-commit overhead, while larger batches increase transaction memory use and recovery cost.

If you know that all your queries are valid, the `resolve()` call can be omitted for even higher performance.

### [](#_security_considerations)Security considerations

*   **Never hardcode credentials**: Use environment variables or secure configuration management
    
*   **Use encryption**: Always enable TLS network encryption for production or publicly accessible deployments
    

## [](#_debugging)Debugging

### [](#_driver_logging)Driver logging

TypeDB drivers support logging via environment variables. Set these before running your application:

 

Variable

Description

`TYPEDB_DRIVER_LOG_LEVEL`

Simple log level (`debug`, `info`, `warn`, `error`, `trace`) applied to all driver components. This is the easiest way to enable logging.

`TYPEDB_DRIVER_LOG`

Fine-grained control using the same syntax as `RUST_LOG`. Allows setting different levels for different components. `TYPEDB_DRIVER_LOG_LEVEL` takes precedence if both are set.

**Examples:**

```bash
# Simple: Enable debug logging for all driver components
export TYPEDB_DRIVER_LOG_LEVEL=debug
python my_app.py

# Advanced: Fine-grained control per component
export TYPEDB_DRIVER_LOG=typedb_driver=debug,typedb_driver_clib=trace
python my_app.py
```

### [](#_enabling_logging_in_your_application)Enabling logging in your application

*   Python
    
*   Java
    
*   Rust
    

Logging is automatically initialized when the driver module is imported. If you want logging enabled before importing the driver (e.g., to capture import-time logs), you can explicitly initialize it first:

```python
from typedb.native_driver_wrapper import init_logging
init_logging()

# Now import and use the driver
from typedb.driver import TypeDB
```

Logging is automatically initialized when driver classes are loaded. If you want logging enabled before creating a driver (e.g., to capture class-loading logs), you can explicitly initialize it first:

```java
import com.typedb.driver.TypeDB;

// Explicitly initialize logging before driver creation
TypeDB.initLogging();

// Now create and use the driver
Driver driver = TypeDB.driver(address, credentials, options);
```

The Rust driver uses the `tracing` crate and does **not** use the `TYPEDB_DRIVER_LOG*` environment variables. Configure your own tracing subscriber using standard Rust logging conventions:

```rust
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

// Initialize tracing with environment filter
tracing_subscriber::registry()
    .with(EnvFilter::from_default_env())
    .with(fmt::layer())
    .init();

// Driver logs will now be captured
let driver = TypeDBDriver::new(address, credential).await?;
```

Set `RUST_LOG=typedb_driver=debug` to enable debug-level driver logs.

## [](#_summary)Summary

Following these best practices will help you build robust TypeDB applications:

1.  **Use automatic resource management** whenever possible
    
2.  **Reuse connections** and handle errors gracefully
    
3.  **Choose appropriate transaction types** and keep transactions short-lived
    
4.  **Batch operations** for better performance
    
5.  **Secure your credentials** and use encryption in production
    
6.  **Enable logging** with environment variables when debugging issues
    

[Analyzing queries](../analyze/index.md) [Tools](../../../tools/index.md)

[Edit on GitHub](https://github.com/typedb/typedb-docs/edit/3.x-development/core-concepts/modules/ROOT/pages/drivers/best-practices.adoc) Edit this page on GitHub.