Hello everyone,
I’m the author of ic-dbms, an open-source Rust framework for building relational databases inside Internet Computer canisters. I released 0.1
Back in December with the core (CRUD, transactions, foreign keys, ACLs, stable memory) and have been shipping fast ever since. Here’s a recap
of the most important features that landed from 0.2 to 0.6.
Just a note: ic-dbms has been refactored and now it is part of a wider project called `wasm-dbms`, which provides an entire DBMS ecosystem for all WebAssembly runtimes, including the Internet Computer. Nothing really changes for IC users, since the library required and the API remain the same, but it opens up a lot of possibilities for cross-platform compatibility and new features in the future.
GitHub: https://github.com/veeso/wasm-dbms
Docs: https://wasm-dbms.cc
Keep Your Data Clean Without Writing a Line of Logic
> Since 0.2, 0.3
ic-dbms now validates and sanitizes fields automatically on every insert and update. Annotate your struct fields and the framework handles the
rest: 14 built-in validators (email, URL, string length, country codes, phone numbers, …) and 12 sanitizers (trim, lowercase, slug, clamp,
UTC normalization, …), plus the ability to write your own custom ones.
pub struct User {
#[primary_key]
pub id: Uint32,
#[sanitizer(TrimSanitizer, LowerCaseSanitizer)]
#[validate(EmailValidator)]
pub email: Text,
#[validate(RangeStrlenValidator(2, 64))]
pub name: Text,
}
No application-level checks needed — bad data gets rejected or cleaned before it ever touches stable memory.
Read more Sanitization Reference | wasm-dbms about the built-in sanitizers.
Read more Validation Reference | wasm-dbms about the built-in validators.
Query JSON Like Postgres
> Since 0.4
JSON columns now support PostgreSQL-inspired filter operators: structural containment (`@>-style`), value extraction at arbitrary paths, and key
existence checks. Paths use dot notation with bracket array indices - `metadata.tags[0].name` just works.
The JSON type has been added as well:
use wasm_dbms_api::prelude::*;
use std::str::FromStr;
#[derive(Debug, Table, Clone, PartialEq, Eq)]
#[table = “products”]
pub struct Product {
#[primary_key]
pub id: Uint32,
pub name: Text,
pub attributes: Json, // {“color”: “red”, “size”: “M”, “tags”: [“sale”, “new”], “price”: 29.99}
}
fn example_queries(database: &impl Database) → Result<(), Box<dyn std::error::Error>> {
// Find all red products
let filter = Filter::json(“attributes”,
JsonFilter::extract_eq(“color”, Value::Text(“red”.into()))
);
let query = Query::builder().filter(filter).build();
let red_products = database.select::<Product>(query)?;
// Find red products with price > 20
let filter = Filter::json(“attributes”, JsonFilter::extract_eq(“color”, Value::Text(“red”.into())))
.and(Filter::json(“attributes”, JsonFilter::extract_gt(“price”, Value::Decimal(20.0.into()))));
let query = Query::builder().filter(filter).build();
let expensive_red = database.select::<Product>(query)?
Ok(())
}
Read more JSON Reference | wasm-dbms about JSON querying and the supported operators.
JOIN Across Tables on Stable Memory
> Since 0.5
Full INNER, LEFT, RIGHT, and FULL join support with qualified column resolution and NULL padding for outer joins. Combined with the existing
filter, ordering, and pagination, queries now feel like a real database.
let query = Query::default()
.join(Join::inner(“posts”, “users.id”, “posts.author_id”))
.order_by(“created_at”, Order::Desc)
.limit(10);
This release also replaced the N+1 pattern on foreign key eager loading with batch fetching. This has increased select performance with eager loading by up to **11.3x when selecting more than 1000 rows**.
Read more Join Engine | wasm-dbms about how joins work under the hood and the optimizations involved.
Define Your Own Column Types
> Since 0.6
`#[derive(CustomDataType)]` lets you create arbitrary column types beyond the built-in set.
#[derive(CustomDataType)]
pub struct Coordinate {
pub lat: f64,
pub lng: f64,
}
#[derive(Table, CandidType, Deserialize, Clone, PartialEq, Eq)]
#[table = “locations”]
pub struct Location {
#[primary_key]
pub id: Uint32,
#[custom_type]
pub position: Coordinate,
}
```
Read more Custom Data Types | wasm-dbms about how to implement your own custom types and the capabilities of the extension system.
Transactions That Don’t Depend on Trap-Revert
> Since 0.6
Transaction rollback is now powered by a write-ahead journal that tracks byte-level changes to stable memory. Rollback is a deterministic
replay of the journal. This makes ic-dbms no more relying on the canister trap mechanism.
Read more Atomicity | wasm-dbms about how the write-ahead journal works and the implications for transaction design.