Crates.io | typedb-driver |
lib.rs | typedb-driver |
version | 3.5.0 |
created_at | 2023-09-28 09:10:36.462546+00 |
updated_at | 2025-09-16 16:52:49.348638+00 |
description | TypeDB Rust Driver |
homepage | https://github.com/typedb/typedb-driver |
repository | https://github.com/typedb/typedb-driver |
max_upload_size | |
id | 985712 |
size | 337,518 |
To learn about the mechanism that TypeDB drivers use set up communication with databases running on the TypeDB Server, refer to the Drivers Overview.
The TypeDB Rust Driver provides a fully async API that supports multiple async runtimes or a synchronous interface gated
by the sync
feature.
To learn about the methods available for executing queries and retrieving their answers using Rust, refer to the API Reference.
typedb-driver
through Cargo:cargo add typedb-driver
tests/integration
for examples):use typedb_driver::TypeDBDriver;
let driver = TypeDBDriver::new_core(TypeDBDriver::DEFAULT_ADDRESS).await.unwrap();
Note: You don't need to compile TypeDB Driver from source if you just want to use it in your code. See the " Quickstart" section above.
Make sure you have Bazel via Bazelisk installed on your machine.
Build the library:
a) to build the native/raw rlib:
bazel build //rust:typedb_driver
The rlib will be produced at: bazel-bin/libtypedb_driver-{hash}.rlib
.
b) to build the crate for a Cargo project:
bazel build //rust:assemble_crate
The Cargo crate will be produced at:
bazel-bin/assemble_crate.crate
You can then unzip this crate to retrieve Cargo.toml
. Please note: this process has not yet been thoroughly
tested. The generated Cargo.toml
may not be fully correct. See the Cargo.toml
of the typedb-driver
crate for
reference.
use std::time::Duration;
use futures::{StreamExt, TryStreamExt};
use typedb_driver::{
answer::{
concept_document::{Leaf, Node},
ConceptRow, QueryAnswer,
},
concept::{Concept, ValueType},
Credentials, DriverOptions, Error, QueryOptions, TransactionOptions, TransactionType, TypeDBDriver,
};
fn typedb_example() {
async_std::task::block_on(async {
// Open a driver connection. Specify your parameters if needed
let driver = TypeDBDriver::new(
TypeDBDriver::DEFAULT_ADDRESS,
Credentials::new("admin", "password"),
DriverOptions::new(false, None).unwrap(),
)
.await
.unwrap();
// Create a database
driver.databases().create("typedb").await.unwrap();
let database = driver.databases().get("typedb").await.unwrap();
// Dropped transactions are closed
{
// Open transactions of 3 types
let transaction = driver.transaction(database.name(), TransactionType::Read).await.unwrap();
// Execute any TypeDB query using TypeQL. Wrong queries are rejected with an explicit error
let result = transaction.query("define entity i-cannot-be-defined-in-read-transactions;").await;
match result {
Ok(_) => println!("This line will not be printed"),
// Handle errors
Err(error) => match error {
Error::Connection(connection) => println!("Could not connect: {connection}"),
Error::Server(server) => {
println!("Received a detailed server error regarding the executed query: {server}")
}
Error::Internal(_) => panic!("Unexpected internal error"),
_ => println!("Received an unexpected external error: {error}"),
},
}
}
// Open a schema transaction to make schema changes
// Transactions can be opened with configurable options. This option limits its lifetime
let options = TransactionOptions::new().transaction_timeout(Duration::from_secs(10));
let transaction =
driver.transaction_with_options(database.name(), TransactionType::Schema, options).await.unwrap();
let define_query = r#"
define
entity person, owns name, owns age;
attribute name, value string;
attribute age, value integer;
"#;
let answer = transaction.query(define_query).await.unwrap();
// Work with the driver's enums in a classic way or using helper methods
if answer.is_ok() && matches!(answer, QueryAnswer::Ok(_)) {
println!("OK results do not give any extra interesting information, but they mean that the query is successfully executed!");
}
// Commit automatically closes the transaction (don't forget to await the result!)
// CAUTION: Committing or closing a transaction will invalidate all its uncollected answer streams
transaction.commit().await.unwrap();
// Open a read transaction to safely read anything without database modifications
let transaction = driver.transaction(database.name(), TransactionType::Read).await.unwrap();
let answer = transaction.query("match entity $x;").await.unwrap();
// Collect concept rows that represent the answer as a table
let rows: Vec<ConceptRow> = answer.into_rows().try_collect().await.unwrap();
let row = rows.get(0).unwrap();
// Retrieve column names to get concepts by index if the variable names are lost
let column_names = row.get_column_names();
let column_name = column_names.get(0).unwrap();
// Get concept by the variable name (column name)
let concept_by_name = row.get(column_name).unwrap().unwrap();
// Get concept by the header's index
let concept_by_index = row.get_index(0).unwrap().unwrap();
// Check if it's an entity type
if concept_by_name.is_entity_type() {
print!("Getting concepts by variable names and indexes is equally correct. ");
println!(
"Both represent the defined entity type: '{}' (in case of a doubt: '{}')",
concept_by_name.get_label(),
concept_by_index.get_label()
);
}
// Continue querying in the same transaction if needed
let answer = transaction.query("match attribute $a;").await.unwrap();
// Concept rows can be used as a stream of results
let mut rows_stream = answer.into_rows();
while let Some(Ok(row)) = rows_stream.next().await {
let mut column_names_iter = row.get_column_names().into_iter();
let column_name = column_names_iter.next().unwrap();
let concept_by_name = row.get(column_name).unwrap().unwrap();
// Check if it's an attribute type to safely retrieve its value type
if concept_by_name.is_attribute_type() {
let label = concept_by_name.get_label();
let value_type = concept_by_name.try_get_value_type().unwrap();
println!("Defined attribute type's label: '{label}', value type: '{value_type}'");
}
println!("It is also possible to just print the concept itself: '{}'", concept_by_name);
}
// Open a write transaction to insert data
let transaction = driver.transaction(database.name(), TransactionType::Write).await.unwrap();
let answer = transaction
.query("insert $z isa person, has age 10; $x isa person, has age 20, has name \"John\";")
.await
.unwrap();
// Insert queries also return concept rows
let mut rows = Vec::new();
let mut rows_stream = answer.into_rows();
while let Some(Ok(row)) = rows_stream.next().await {
rows.push(row);
}
let row = rows.get(0).unwrap();
for column_name in row.get_column_names() {
let inserted_concept = row.get(column_name).unwrap().unwrap();
println!("Successfully inserted ${}: {}", column_name, inserted_concept);
if inserted_concept.is_entity() {
println!("This time, it's an entity, not a type!");
}
}
// It is possible to ask for the column names again
let column_names = row.get_column_names();
let x = row.get_index(column_names.iter().position(|r| r == "x").unwrap()).unwrap().unwrap();
if let Some(iid) = x.try_get_iid() {
println!("Each entity receives a unique IID. It can be retrieved directly: {}", iid);
}
// Do not forget to commit if the changes should be persisted
transaction.commit().await.unwrap();
// Open another write transaction to try inserting even more data
let transaction = driver.transaction(database.name(), TransactionType::Write).await.unwrap();
// When loading a large dataset, it's often better not to resolve every query's promise immediately.
// Instead, collect promises and handle them later. Alternatively, if a commit is expected in the end,
// just call `commit`, which will wait for all ongoing operations to finish before executing.
let queries = ["insert $a isa person, has name \"Alice\";", "insert $b isa person, has name \"Bob\";"];
for query in queries {
transaction.query(query);
}
transaction.commit().await.unwrap();
{
let transaction = driver.transaction(database.name(), TransactionType::Write).await.unwrap();
// Commit will still fail if at least one of the queries produce an error.
let queries =
["insert $c isa not-person, has name \"Chris\";", "insert $d isa person, has name \"David\";"];
let mut promises = vec![];
for query in queries {
promises.push(transaction.query(query));
}
let result = transaction.commit().await;
println!("Commit result will contain the unresolved query's error: {}", result.unwrap_err());
}
// Open a read transaction to verify that the inserted data is saved
let transaction = driver.transaction(database.name(), TransactionType::Read).await.unwrap();
// A match query can be used for concept row outputs
// Queries can also be executed with configurable options. This option forces the database
// to include types of instance concepts in ConceptRows answers
let options = QueryOptions::new().include_instance_types(true);
let var = "x";
let answer = transaction.query_with_options(format!("match ${} isa person;", var), options).await.unwrap();
// Simple match queries always return concept rows
let mut count = 0;
let mut stream = answer.into_rows().map(|result| result.unwrap());
while let Some(row) = stream.next().await {
let x = row.get(var).unwrap().unwrap();
match x {
Concept::Entity(x_entity) => {
let x_type = x_entity.type_().unwrap();
count += 1;
println!("Found a person {} of type {}", x, x_type);
}
_ => unreachable!("An entity is expected"),
}
}
println!("Total persons found: {}", count);
// A fetch query can be used for concept document outputs with flexible structure
let fetch_query = r#"
match
$x isa! person, has $a;
$a isa! $t;
fetch {
"single attribute type": $t,
"single attribute": $a,
"all attributes": { $x.* },
};
"#;
let answer = transaction.query(fetch_query).await.unwrap();
// Fetch queries always return concept documents
let mut count = 0;
let mut stream = answer.into_documents().map(|result| result.unwrap());
while let Some(document) = stream.next().await {
// The content of the document can be explored in details
match document.root.as_ref().unwrap() {
Node::Map(map) => {
println!("Found a map document:\n{{");
for (parameter_name, node) in map {
print!(" \"{parameter_name}\": ");
match node {
Node::Map(map) => println!("map of {} element(s)", map.len()),
Node::Leaf(leaf) => match leaf.as_ref().unwrap() {
Leaf::Concept(concept) => match concept {
Concept::AttributeType(type_) => println!("attribute type '{}'", type_.label()),
Concept::Attribute(attribute) => println!("attribute '{}'", attribute.value),
_ => unreachable!("Unexpected concept is fetched"),
},
_ => unreachable!("Unexpected leaf type is fetched"),
},
_ => unreachable!("Expected lists in inner maps"),
}
print!("")
}
println!("}}");
}
_ => unreachable!("Unexpected document type is fetched"),
}
// The document can be converted to a JSON
count += 1;
println!("JSON representation of the fetched document:\n{}", document.into_json().to_string());
}
println!("Total documents fetched: {}", count);
println!("More examples can be found in the API reference and the documentation.\nWelcome to TypeDB!");
})
}