Rust with embedded SurrealDB
SurrealDB on it's own is no doubt one of the more exciting projects I've seen in recent years. However when used as an embedded database it opens up a whole world of possibilities. Especially when you're working within the context of a Rust project.
The official guide on how to embed SurrealDB into Rust is a great starting point. The rest is reasonably well covered on the docs.rs pages. The following example helps putting together the puzzle explaining how to set up a database, run queries and use the various datatypes you get within your result.
My cargo.toml
:
[package]
name = "surrealdb-playground"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = { version = "1.0.75" }
surrealdb = { version = "1.0.0", features = ["kv-mem", "default"] }
tokio = { version = "1.32.0", features = ["full"] }
And a simple main.rs
:
use anyhow::Result;
// https://doc.rust-lang.org/std/collections/struct.HashMap.html
use std::collections::HashMap;
// https://docs.rs/surrealdb/latest/surrealdb/sql/struct.Datetime.html
use surrealdb::sql::Datetime;
// https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html
use surrealdb::Surreal;
// https://docs.rs/surrealdb/latest/surrealdb/engine/local/struct.Mem.html
use surrealdb::engine::local::Mem;
// https://docs.rs/surrealdb/latest/surrealdb/opt/type.RecordId.html
use surrealdb::opt::RecordId;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize in memory database. Other options include various file based backends.
// See: https://docs.rs/surrealdb/latest/surrealdb/engine/local/index.html
// Namespace (NS) and Database (DB) can be random. More about namespaces and databases:
// https://surrealdb.com/docs/introduction/concepts
let db = Surreal::new::<Mem>(()).await?;
db.use_ns("ns1").use_db("db1").await?;
// SurrealQL for creating a task with given ID 1.
// More about SurrealQL: https://surrealdb.com/docs/surrealql
let sql = "\
CREATE task:1 SET \
title='Task 01', \
priority=10, \
created=time::now(), \
items=['eating', 'sleeping'], \
permissions={'me': 1, 'mom': 0 }\
";
// Running the SurQL code from above expecting a SurrealDB Response:
// https://docs.rs/surrealdb/latest/surrealdb/struct.Response.html
//
// More about the query function:
// https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html#method.query
let mut res = db.query(sql).await?;
// Check the result to see what kind of data you get.
// dbg!(&res);
// Taking various fields from the response. This works with all responses (e.g. SELECT)
// More: https://docs.rs/surrealdb/latest/surrealdb/struct.Response.html#method.take
let id: Option<RecordId> = res.take("id")?;
let title: Option<String> = res.take("title")?;
let priority: Option<u32> = res.take("priority")?;
let created: Option<Datetime> = res.take("created")?;
let items: Option<Vec<String>> = res.take("items")?;
let permissions: Option<HashMap<String, u32>> = res.take("permissions")?;
dbg!(&id, &title, &priority, &created, &items, &permissions);
// Playing around with data. For example converting DateTime to a UNIX timestamp
// https://docs.rs/surrealdb/latest/surrealdb/sql/struct.Datetime.html#method.timestamp
let timestamp = created
.is_some()
.then(|| created.to_owned().unwrap().timestamp());
dbg!(×tamp);
Ok(())
}
Rust vs. other Languages
SurrealDB can be embedded into various other languages. One of the most exciting alternatives is the WASM build allowing the use of embedded SurrealDB within browser environments. Exciting stuff indeed.
Yet SurrealDB is written in Rust. Embedding it into any other Rust project is seamless. You can completely skip any need for an external server without any loss in functionality. Instead of dealing with an external dependency your data can live right inside your application combined with a very powerful query language.
Work in Progress
This article is a bit of a work in progress project as I'm just starting with a more serious project using SurrealDB inside of Rust. Stay tuned for more examples.