Skip to main content
For the complete documentation index optimized for AI agents, see llms.txt.
Queries for HelixDB are authored in Rust using the helix-db crate (imported as helix_db). This page covers bootstrapping a Rust project that builds your queries into the queries.json bundle and lets you send them as dynamic requests. For the traversal model and query patterns themselves, see Querying and the docs.rs reference.

Prerequisites

  • A recent stable Rust toolchain — install via rustup if you don’t already have it.
  • Optional: the Helix CLI for running the resulting bundle against a local instance.

Create a new Rust project

generate_to_path is invoked from main, so a binary crate is the simplest layout:
cargo new helix-queries
cd helix-queries
You can also add this as a binary inside an existing Cargo workspace if you’d rather keep your queries alongside the rest of your service code.

Add the dependency

cargo add helix-db
Or add it directly to Cargo.toml:
[dependencies]
helix-db = "2.0"
The crate is published on crates.io as helix-db but its module path is helix_db, so all imports look like use helix_db::....

Set up src/main.rs

fn main() {
    let path = helix_db::generate_to_path("queries.json").expect("generate queries bundle");
    println!("generated {}", path.display());
}
generate_to_path collects every query you’ve defined in the crate and writes a single queries.json bundle to the path you pass. It returns the resolved path so you can log it or hand it to a downstream deploy step.

Importing the DSL

Inside the modules where you author queries, bring the DSL into scope with the prelude:
use helix_db::dsl::prelude::*;
The prelude re-exports the common building blocks — read_batch, write_batch, g(), sub(), NodeRef/EdgeRef, Predicate/SourcePredicate, the projection helpers, and more. See the docs.rs reference for the full surface.

Setting up your queries

Queries are defined as top-level pub fn items annotated with #[register]. Each function returns either a WriteBatch (for mutations) or a ReadBatch (for read-only traversals), and generate_to_path picks them up automatically when the bundle is built. The function arguments become the query’s named parameters at the HTTP edge. For a small project you can keep everything in src/main.rs alongside fn main(); once you have more than a handful of queries, move them into their own modules.
use helix_db::dsl::prelude::*;

// Write query: insert a new User node.
#[register]
pub fn add_user(userId: String, name: String) -> WriteBatch {
    // The macro reads parameter *names* at compile time, so the values themselves
    // go unused in the body; this line silences the unused-variable warning.
    let _ = (&userId, &name);
    write_batch()
        .var_as(
            "newUser",
            g().add_n(
                "User",
                vec![
                    ("userId", PropertyInput::param("userId")),
                    ("name",   PropertyInput::param("name")),
                ],
            )
            .project(vec![PropertyProjection::renamed("$id", "id")]),
        )
        .returning(["newUser"])
}

// Read query: fetch a single User by id.
#[register]
pub fn user_by_id(userId: String) -> ReadBatch {
    let _ = &userId;
    read_batch()
        .var_as(
            "user",
            g().n_with_label("User")
                .where_(Predicate::eq_param("userId", "userId"))
                .project(vec![
                    PropertyProjection::renamed("$id", "id"),
                    PropertyProjection::new("name"),
                ]),
        )
        .returning(["user"])
}

fn main() {
    let path = helix_db::generate_to_path("queries.json").expect("generate queries bundle");
    println!("generated {}", path.display());
}
A few things to know about this snippet:
  • #[register] adds the function to the global bundle, so generate_to_path picks it up — no manual list to maintain.
  • Calling the function with concrete arguments returns a DynamicQueryRequest you POST to /v1/query; the request’s query_name is set to the function name (so logs show add_user, not __dynamic__). See Querying for the send path.
For the full builder catalog — edges, predicates, vector/text search, sub-traversals, aggregations — see the docs.rs reference.

Generate the bundle

cargo run
This produces queries.json in the project root. From here you can:

Next Steps

Querying

Traversal DSL, dynamic queries, and transactions.

Working with HelixDB

Deploy queries.json and the runtime workflow.

Local Development

Run the bundle locally with the enterprise-dev image.

DSL API Reference

Full helix_db API on docs.rs.