gRPC Rust Client
Rust set-up
Set up a Rust project
Use the following command to initialize a Rust project in an empty directory:
cargo init
Two files will be created, Cargo.toml
and src/main.rs
.
Add dependencies
-
Add required dependencies to the
Cargo.toml
file in your Rust project. You’ll need at leaststargate-grpc
and an async framework, such as tokio:[dependencies] stargate-grpc = "0.3" tokio = { version = "1", features = ["full"]}
-
Build the project with
cargo build
and fetch and compile the dependencies. -
Add the following line to the includes in the source code of your app, such as
main.rs
:use stargate_grpc::*;
This set-up will make all the Stargate gRPC functionality available.
The next sections explain the parts of a script to use the Stargate functionality. A full working script is included below.
Rust connecting
Authentication
This example assumes that you’re running Stargate locally with the default credentials
of cassandra/cassandra
.
For more information regarding authentication please see the
Stargate authentication and authorization docs.
You’ll need to generate a token to insert into the client connection code:
curl -L -X POST 'http://localhost:8081/v1/auth' \
-H 'Content-Type: application/json' \
--data-raw '{
"username": "cassandra",
"password": "cassandra"
}'
Set up client
The main structure that provides the interface to Stargate is StargateClient
.
The simplest way to obtain an instance is to use the provided builder
:
use stargate_grpc::*;
use std::str::FromStr;
// Set the Stargate OSS configuration for a locally running docker container:
let sg_uri = "https://localhost:8090/";
let auth_token = "06251024-5aeb-4200-a132-5336e73e5b6e";
// For Stargate OSS: create a client
let mut client = StargateClient::builder()
.uri(sg_uri)?
.auth_token(AuthToken::from_str(auth_token)?)
.connect()
.await?;
println!("created client {:?}", client);
Rust querying
Use QueryBuilder to create a query, bind query values and pass query parameters. The query is followed by the execute commands that actually run the command and return the results.
// For Stargate OSS: SELECT the data to read from the table
// Select/query some data from the keyspace.table
let query = Query::builder()
// Set the keyspace for the the query
.keyspace("test")
// Set consistency level
.consistency(Consistency::One)
.query("SELECT firstname, lastname FROM test.users;")
// Build the query
.build();
println!("select executed");
use std::convert::TryInto;
// Send the query and wait for gRPC response
let response = client.execute_query(query).await?;
// Convert the response into a ResultSet
let result_set: ResultSet = response.try_into()?;
It is also possible to use a bind
statement to insert values:
.query("SELECT login, emails FROM users WHERE id = :id")
.bind_value("id", 1000)
Data definition language (DDL) queries are supported in the same manner:
// For Stargate OSS only: create a keyspace
let create_keyspace = Query::builder()
.query("CREATE KEYSPACE IF NOT EXISTS test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};")
.build();
client.execute_query(create_keyspace).await?;
println!("created keyspace");
// For Stargate OSS: create a table
let create_table = Query::builder()
.query(
"CREATE TABLE IF NOT EXISTS test.users \
(firstname text, lastname text, PRIMARY KEY (firstname, lastname));",
)
.build();
client.execute_query(create_table).await?;
println!("created table");
In general, users will create a keyspace and table first.
The ExecuteQuery
function can be used to execute a single query.
If you need to group several commands together as a
batch statement,
the client also provides an ExecuteBatch()
function to execute a batch query:
// For Stargate OSS: INSERT two rows/records
// Two queries will be run in a batch statement
let batch = Batch::builder()
.keyspace("test") // set the keyspace the query applies to
.consistency(Consistency::One) // set consistency level
.query("INSERT INTO test.users (firstname, lastname) VALUES ('Jane', 'Doe');")
.query("INSERT INTO test.users (firstname, lastname) VALUES ('Serge', 'Provencio');")
.build();
client.execute_batch(batch).await?;
println!("insert data");
This example inserts two values into the keyspace table test.users
.
Only INSERT
, UPDATE
, and DELETE
operations can be used in a batch query.
Rust processing result set
The result set comes back as a collection of rows. A row can be easily unpacked:
// This for loop to get the results
for row in result_set.rows {
let (firstname, lastname): (String, String) = row.try_into()?;
println!("{} {}", firstname, lastname);
}
Rust full sample script
To put all the pieces together, here is a sample script that combines all the pieces shown above:
use stargate_grpc::*;
use std::str::FromStr;
use std::error::Error;
use std::convert::TryInto;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// For Stargate OSS: create a client
let mut client = StargateClient::builder()
// For Stargate OSS running locally in docker container, set connect information:
.uri("https://localhost:8090/")? // replace with a proper address
.auth_token(AuthToken::from_str("721e9c04-e121-4bf4-b9a6-887ebeae2bc5")?) // replace with a proper token
.connect()
.await?;
println!("created client {:?}", client);
// For Stargate OSS only: create a keyspace
let create_keyspace = Query::builder()
.query("CREATE KEYSPACE IF NOT EXISTS test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};")
.build();
client.execute_query(create_keyspace).await?;
println!("created keyspace");
// For Stargate OSS: create a table
let create_table = Query::builder()
// .keyspace("test")
.query(
"CREATE TABLE IF NOT EXISTS test.users \
(firstname text, lastname text, PRIMARY KEY (firstname, lastname));",
)
.build();
client.execute_query(create_table).await?;
println!("created table");
// For Stargate OSS: INSERT two rows/records
// Two queries will be run in a batch statement
let batch = Batch::builder()
.keyspace("test") // set the keyspace the query applies to
.consistency(Consistency::One) // set consistency level
.query("INSERT INTO test.users (firstname, lastname) VALUES ('Lorina', 'Poland');")
.query("INSERT INTO test.users (firstname, lastname) VALUES ('Doug', 'Wettlaufer');")
.build();
client.execute_batch(batch).await?;
println!("insert data");
// For Stargate OSS: SELECT the data to read from the table
// Select/query some data from the keyspace.table
let query = Query::builder()
.keyspace("test")
.consistency(Consistency::One)
.query("SELECT firstname, lastname FROM test.users;")
.build();
println!("select executed");
// Get the results from the execute query statement and convert into a ResultSet
let response = client.execute_query(query).await?;
let result_set: ResultSet = response.try_into()?;
// This for loop to get the results
for row in result_set.rows {
let (firstname, lastname): (String, String) = row.try_into()?;
println!("{} {}", firstname, lastname);
}
println!("everything worked!");
Ok(())
}
created client StargateClient { inner: Grpc { inner: InterceptedService { inner: Channel, f: stargate_grpc::client::AuthToken } } }
created keyspace
created table
insert data
select executed
Jane Doe
Serge Provencio
everything worked!
Rust developing
Building
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://github.com/stargate/stargate-grpc-rust-client stargate-grpc
cd stargate-grpc
cargo build
Running the examples
For your convenience, this project contains a bunch of examples located in the examples directory, which demonstrate connecting, creating schemas, inserting data and querying. Each example program accepts an URL of the Stargate coordinator, the authentication token and the keyspace name:
cargo run --example <example> [-- [--keyspace <keyspace>] [--token <token>] [--tls] [<url>]]
The authentication token value can be also defined in the SG_TOKEN environment variable.
You’ll need to start a Stargate instance.
Start Stargate
This method is not required for Stargate v2, but you need these instructions for Stargate v1.
If you don’t already have access to a Stargate deployment, one can be started
quickly for testing in developer mode.
Developer mode removes the need to set up a separate Cassandra instance and is
meant for development and testing only.
This docker run
command also exposes port 8090 for gRPC connections.
docker run --name stargate \
-p 8080:8080 \
-p 8081:8081 \
-p 8082:8082 \
-p 8090:8090 \
-p 127.0.0.1:9042:9042 \
-d \
-e CLUSTER_NAME=stargate \
-e CLUSTER_VERSION=4.0 \
-e DEVELOPER_MODE=true \
stargateio/stargate-4_0:v1.0.57
Ensure the local instance of Stargate is running properly by tailing the logs for the container, and looking for the message that indicates Stargate is ready for traffic.
docker logs -f stargate | grep "Finished starting bundles."
Finished starting bundles.
Run examples
Run the keyspace
example to test the connection and create the test
keyspace with the default keyspace name of stargate_examples
:
cargo run --example keyspace
Connected to https://127.0.0.2:8090
Created keyspace stargate_examples
Run the query
example to create a keyspace and table, insert some data, and
select data to check the insertion:
cargo run --example query
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/examples/basic`
Connected to https://127.0.0.2:8090
Created schema
Inserted data. Now querying.
All rows:
2 user_2 ["user_2@example.net", "user_2@mail.example.net"]
3 user_3 ["user_3@example.net", "user_3@mail.example.net"]
7 user_7 ["user_7@example.net", "user_7@mail.example.net"]
9 user_9 ["user_9@example.net", "user_9@mail.example.net"]
4 user_4 ["user_4@example.net", "user_4@mail.example.net"]
0 user_0 ["user_0@example.net", "user_0@mail.example.net"]
8 user_8 ["user_8@example.net", "user_8@mail.example.net"]
5 user_5 ["user_5@example.net", "user_5@mail.example.net"]
6 user_6 ["user_6@example.net", "user_6@mail.example.net"]
1 user_1 ["user_1@example.net", "user_1@mail.example.net"]
Row with id = 1:
1 user_1 ["user_1@example.net", "user_1@mail.example.net"]
The Stargate gRPC Rust Client repository is located at https://github.com/stargate/stargate-grpc-rust-client.