| Crates.io | surqx |
| lib.rs | surqx |
| version | 0.1.1 |
| created_at | 2025-07-13 15:07:37.117186+00 |
| updated_at | 2025-07-13 16:39:55.406618+00 |
| description | Query macro for SurrealDB |
| homepage | |
| repository | https://github.com/bysensa/surqx |
| max_upload_size | |
| id | 1750570 |
| size | 130,332 |
A Rust library that provides a convenient macro for writing SurrealDB queries with variable interpolation and type-safe parameter binding.
sql! macro with embedded Rust variables&variable syntax to safely interpolate variables into queriesP.S: A huge thank you to @m-ou-se for the work done on the inline-python library, which inspired the implementation of this solution.
Add this to your Cargo.toml:
[dependencies]
surqx = "0.1.0"
surrealdb = "2.3.7"
tokio = { version = "1", features = ["full"] }
use surqx::sql;
use surrealdb::engine::any::connect;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = connect("mem://").await?;
db.use_ns("test").use_db("test").await?;
let name = "John";
let age = 30;
let (query, vars) = sql! {
CREATE person SET name = &name, age = &age;
SELECT * FROM person WHERE name = &name;
};
let result = db.query(query).bind(vars).await?;
let result = result.check()?;
println!("{:?}", result);
Ok(())
}
use surqx::sql;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Person {
pub name: String,
pub age: usize,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = connect("mem://").await?;
db.use_ns("test").use_db("test").await?;
let persons = vec![
Person {
name: "Jane".to_string(),
age: 23,
},
Person {
name: "John".to_string(),
age: 32,
},
];
let (query, vars) = sql! {
BEGIN TRANSACTION;
FOR $person IN &persons {
CREATE type::thing("person", $person.name) CONTENT {
name: $person.name,
age: $person.age,
};
};
COMMIT TRANSACTION;
SELECT * FROM person;
};
let result = db.query(query).bind(vars).await?;
let result = result.check()?;
println!("{:?}", result);
Ok(())
}
use surqx::sql;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = connect("mem://").await?;
db.use_ns("demo").use_db("demo").await?;
let (query, vars) = sql! {
CREATE person:aristotle, article:on_sleep_and_sleeplessness;
RELATE person:aristotle->wrote->article:on_sleep_and_sleeplessness;
LET $time = time::now();
RELATE person:author->wrote->article:demo
CONTENT {
time: {
written: $time
}
};
SELECT * FROM person WHERE ->knows->person->(knows WHERE influencer = true) TIMEOUT 5s;
SELECT ->purchased->product<-purchased<-person->purchased->product FROM person:tobie PARALLEL;
SELECT
name,
->(wrote WHERE written_at = "Athens")->book.{ name, id } AS books_written_in_athens
FROM person;
};
let result = db.query(query).bind(vars).await?;
Ok(())
}
The sql! macro processes your SurrealDB queries at compile time and:
&variable references in your queryVars struct for bindingUse the & prefix to interpolate Rust variables into your queries:
let user_id = "john_doe";
let min_age = 18;
let (query, vars) = sql! {
SELECT * FROM users
WHERE id = &user_id AND age >= &min_age;
};
This is equivalent to:
let (query, vars) = sql! {
SELECT * FROM users
WHERE id = $user_id_abc123 AND age >= $min_age_def456;
};
// Where vars contains the serialized values for user_id and min_age
This library consists of two main components:
Vars type and re-exports the macroThe macro uses a custom tokenizer to:
The library provides compile-time error checking for:
Runtime errors are handled through the Vars type, which collects serialization errors and reports them when the query is executed.
This project is licensed under the Apache 2.0 License.
Contributions are welcome! Please feel free to submit a Pull Request.