Skip to main content

Overview

This quickstart guide will walk you through creating a simple application that connects to ScyllaDB, creates a keyspace and table, inserts data, and queries it back.
This guide assumes you have a ScyllaDB or Cassandra cluster running. If you don’t, see the ScyllaDB documentation for setup instructions.

Prerequisites

1

Install Rust

Ensure you have Rust 1.85.0 or later installed:
rustc --version
2

Create a new project

cargo new scylla-quickstart
cd scylla-quickstart
3

Add dependencies

Add the following to your Cargo.toml:
[dependencies]
scylla = "1.5"
tokio = { version = "1.40", features = ["full"] }
futures = "0.3"
anyhow = "1.0"

Basic Connection

Let’s start by connecting to your ScyllaDB cluster. Replace the code in src/main.rs:
src/main.rs
use anyhow::Result;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::env;

#[tokio::main]
async fn main() -> Result<()> {
    // Get ScyllaDB URI from environment or use default
    let uri = env::var("SCYLLA_URI")
        .unwrap_or_else(|_| "127.0.0.1:9042".to_string());

    println!("Connecting to {uri} ...");

    // Create a session
    let session: Session = SessionBuilder::new()
        .known_node(uri)
        .build()
        .await?;

    println!("Connected successfully!");

    Ok(())
}
Run your application:
cargo run
Set the SCYLLA_URI environment variable to connect to a different node:
SCYLLA_URI="192.168.1.100:9042" cargo run

Creating Schema

Now let’s create a keyspace and table:
src/main.rs
use anyhow::Result;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;

#[tokio::main]
async fn main() -> Result<()> {
    let uri = std::env::var("SCYLLA_URI")
        .unwrap_or_else(|_| "127.0.0.1:9042".to_string());

    println!("Connecting to {uri} ...");

    let session: Session = SessionBuilder::new()
        .known_node(uri)
        .build()
        .await?;

    // Create a keyspace
    session
        .query_unpaged(
            "CREATE KEYSPACE IF NOT EXISTS my_keyspace WITH REPLICATION = {
                'class' : 'NetworkTopologyStrategy',
                'replication_factor' : 1
            }",
            &[],
        )
        .await?;

    // Create a table
    session
        .query_unpaged(
            "CREATE TABLE IF NOT EXISTS my_keyspace.users (
                user_id int,
                username text,
                email text,
                PRIMARY KEY (user_id)
            )",
            &[],
        )
        .await?;

    println!("Schema created successfully!");

    Ok(())
}
The query_unpaged method executes a query and fetches all results in a single page. For queries that might return many rows, use query_iter instead.

Inserting Data

Insert data using parameterized queries to prevent CQL injection:
// Insert with simple query and parameters
session
    .query_unpaged(
        "INSERT INTO my_keyspace.users (user_id, username, email) VALUES (?, ?, ?)",
        (1, "alice", "[email protected]"),
    )
    .await?;

session
    .query_unpaged(
        "INSERT INTO my_keyspace.users (user_id, username, email) VALUES (?, ?, ?)",
        (2, "bob", "[email protected]"),
    )
    .await?;
Use prepared statements for queries that will be executed multiple times. They are parsed once on the server and provide better performance.

Querying Data

Query data and deserialize results into Rust types:
use futures::TryStreamExt;

// Query and parse rows as tuples
let mut iter = session
    .query_iter("SELECT user_id, username, email FROM my_keyspace.users", &[])
    .await?
    .rows_stream::<(i32, String, String)>()?;

while let Some((user_id, username, email)) = iter.try_next().await? {
    println!("User {}: {} ({})", user_id, username, email);
}

Complete Example

Here’s a complete working example combining all the concepts:
src/main.rs
use anyhow::Result;
use futures::TryStreamExt;
use scylla::DeserializeRow;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::env;

#[derive(Debug, DeserializeRow)]
struct User {
    user_id: i32,
    username: String,
    email: String,
}

#[tokio::main]
async fn main() -> Result<()> {
    let uri = env::var("SCYLLA_URI")
        .unwrap_or_else(|_| "127.0.0.1:9042".to_string());

    println!("Connecting to {uri} ...");

    let session: Session = SessionBuilder::new()
        .known_node(uri)
        .build()
        .await?;

    // Create keyspace
    session
        .query_unpaged(
            "CREATE KEYSPACE IF NOT EXISTS my_keyspace WITH REPLICATION = {
                'class' : 'NetworkTopologyStrategy',
                'replication_factor' : 1
            }",
            &[],
        )
        .await?;

    // Create table
    session
        .query_unpaged(
            "CREATE TABLE IF NOT EXISTS my_keyspace.users (
                user_id int,
                username text,
                email text,
                PRIMARY KEY (user_id)
            )",
            &[],
        )
        .await?;

    // Prepare insert statement
    let prepared = session
        .prepare("INSERT INTO my_keyspace.users (user_id, username, email) VALUES (?, ?, ?)")
        .await?;

    // Insert data
    session
        .execute_unpaged(&prepared, (1, "alice", "[email protected]"))
        .await?;
    session
        .execute_unpaged(&prepared, (2, "bob", "[email protected]"))
        .await?;
    session
        .execute_unpaged(&prepared, (3, "charlie", "[email protected]"))
        .await?;

    println!("Inserted 3 users");

    // Query data
    let mut iter = session
        .query_iter("SELECT user_id, username, email FROM my_keyspace.users", &[])
        .await?
        .rows_stream::<User>()?;

    println!("\nUsers in database:");
    while let Some(user) = iter.try_next().await? {
        println!("  - User {}: {} ({})", user.user_id, user.username, user.email);
    }

    Ok(())
}
Run the complete example:
SCYLLA_URI="127.0.0.1:9042" cargo run

Advanced Features

Now that you’ve built a basic application, explore these advanced features:

User-Defined Types

Define custom types that map to CQL UDTs:
use scylla::{DeserializeValue, SerializeValue};

#[derive(Debug, DeserializeValue, SerializeValue)]
struct Address {
    street: String,
    city: String,
    zip_code: i32,
}

// Create the UDT in ScyllaDB
session
    .query_unpaged(
        "CREATE TYPE IF NOT EXISTS my_keyspace.address (
            street text,
            city text,
            zip_code int
        )",
        &[],
    )
    .await?;

// Use it in a table
session
    .query_unpaged(
        "CREATE TABLE IF NOT EXISTS my_keyspace.user_addresses (
            user_id int,
            address frozen<address>,
            PRIMARY KEY (user_id)
        )",
        &[],
    )
    .await?;

// Insert data
let address = Address {
    street: "123 Main St".to_string(),
    city: "Springfield".to_string(),
    zip_code: 12345,
};

session
    .query_unpaged(
        "INSERT INTO my_keyspace.user_addresses (user_id, address) VALUES (?, ?)",
        (1, address),
    )
    .await?;

Metrics and Monitoring

Track driver performance with built-in metrics:
let metrics = session.get_metrics();

println!("Queries requested: {}", metrics.get_queries_num());
println!("Errors occurred: {}", metrics.get_errors_num());
println!("Average latency: {} ms", metrics.get_latency_avg_ms()?);
println!("99.9th percentile: {} ms", metrics.get_latency_percentile_ms(99.9)?);
println!("Total connections: {}", metrics.get_total_connections());
Enable the metrics feature flag in your Cargo.toml to use metrics functionality.

Authentication

Connect to a cluster with authentication:
let session = SessionBuilder::new()
    .known_node("127.0.0.1:9042")
    .user("cassandra", "cassandra")
    .build()
    .await?;

Batch Statements

Execute multiple statements atomically:
use scylla::statement::batch::Batch;

let mut batch = Batch::default();
batch.append_statement("INSERT INTO my_keyspace.users (user_id, username, email) VALUES (?, ?, ?)");
batch.append_statement("INSERT INTO my_keyspace.users (user_id, username, email) VALUES (?, ?, ?)");

session
    .batch(
        &batch,
        ((4, "dave", "[email protected]"), (5, "eve", "[email protected]")),
    )
    .await?;

Next Steps

Connection

Learn about connection pooling and cluster discovery

Queries

Master different query types and execution patterns

Type System

Understand CQL to Rust type mappings

Examples

Explore more examples in the repository

Build docs developers (and LLMs) love