| Crates.io | ic-nosql |
| lib.rs | ic-nosql |
| version | 0.1.3 |
| created_at | 2025-08-05 08:06:59.470071+00 |
| updated_at | 2025-08-28 07:43:51.668542+00 |
| description | A flexible NoSQL database library for Internet Computer canisters |
| homepage | |
| repository | https://github.com/mycel-labs/atp |
| max_upload_size | |
| id | 1781701 |
| size | 69,811 |
A type-safe NoSQL database library for Internet Computer canisters with automatic memory management.
IC-NoSQL provides a high-level interface for storing and querying structured data in Internet Computer canisters. It handles memory allocation, serialization, and provides type-safe operations with pagination support.
use ic_nosql::{define_model, CandidType};
use serde::{Deserialize, Serialize};
define_model! {
#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub struct User {
pub id: String,
pub username: String,
pub email: String,
pub created_at: u64,
}
primary_key: id -> String,
}
define_model! {
#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub struct Post {
pub id: String,
pub user_id: String,
pub title: String,
pub content: String,
pub created_at: u64,
}
primary_key: id -> String,
secondary_key: user_id -> String,
}
use ic_nosql::DatabaseManager;
thread_local! {
static DB_MANAGER: RefCell<Option<DatabaseManager>> = RefCell::new(None);
}
#[ic_cdk::init]
fn init() {
let mut db_manager = DatabaseManager::new();
// Register models with unique memory IDs
db_manager.register_model("users", Some(10), None).expect("Failed to register users");
db_manager.register_model("posts", Some(11), None).expect("Failed to register posts");
DB_MANAGER.with(|db| *db.borrow_mut() = Some(db_manager));
}
// Create
let user = User {
id: "user_123".to_string(),
username: "alice".to_string(),
email: "alice@example.com".to_string(),
created_at: ic_cdk::api::time(),
};
DB_MANAGER.with(|db_manager| {
let mut db = db_manager.borrow_mut();
let db = db.as_mut().ok_or("Database not initialized")?;
db.insert("users", &user.id, &user)
})?;
// Read
let user = DB_MANAGER.with(|db_manager| {
let db = db_manager.borrow();
let db = db.as_ref().ok_or("Database not initialized")?;
db.get::<User>("users", "user_123")?
.ok_or("User not found")
})?;
// Query with pagination
let response = DB_MANAGER.with(|db_manager| {
let db = db_manager.borrow();
let db = db.as_ref().ok_or("Database not initialized")?;
db.query::<User>("users", 10, 1)
})?;
// Update
let mut updated_user = user.clone();
updated_user.email = "newemail@example.com".to_string();
DB_MANAGER.with(|db_manager| {
let db = db_manager.borrow();
let db = db.as_ref().ok_or("Database not initialized")?;
db.update("users", &updated_user.id, &updated_user)
})?;
// Delete
DB_MANAGER.with(|db_manager| {
let db = db_manager.borrow();
let db = db.as_ref().ok_or("Database not initialized")?;
db.delete("users", "user_123")
})?;
register_model(name: &str, memory_id: Option<u8>, max_size: Option<u32>) -> Result<()>insert<T: Model>(collection: &str, id: &str, data: &T) -> Result<()>get<T: Model>(collection: &str, id: &str) -> Result<Option<T>>update<T: Model>(collection: &str, id: &str, data: &T) -> Result<()>delete(collection: &str, id: &str) -> Result<()>query<T: Model>(collection: &str, limit: usize, page: usize) -> Result<QueryResponse<T>>stats() -> Vec<String>Use the define_model! macro to automatically implement the Model trait:
define_model! {
#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub struct MyModel {
pub id: String,
pub name: String,
pub category: String,
}
primary_key: id -> String,
secondary_key: category -> String,
}
// Or for simple models without secondary keys:
define_model! {
#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
pub struct SimpleModel {
pub id: String,
pub data: String,
}
primary_key: id -> String,
}
IC-NoSQL uses stable memory with automatic allocation:
The package includes comprehensive tests and stress tests:
# Build example canister
cargo build --package example-canister --target wasm32-unknown-unknown --release
# Run stress tests (requires PocketIC)
export POCKET_IC_BIN=$(which pocket-ic)
cargo test --package ic-nosql-tests --test stress_tests
See example-canister/ for a complete implementation demonstrating:
create_user(username: text, email: text) -> Result<User, text>get_user(id: text) -> Result<User, text>list_users(page: nat, size: nat) -> Result<vec User, text>create_post(user_id: text, title: text, content: text) -> Result<Post, text>get_post(id: text) -> Result<Post, text>list_posts(page: nat, size: nat) -> Result<vec Post, text>create_comment(post_id: text, user_id: text, content: text) -> Result<Comment, text>get_comment(id: text) -> Result<Comment, text>list_comments(page: nat, size: nat) -> Result<vec Comment, text>init() or post_upgrade()ic_cdk::println! for debugging in canistersstats() methodIf using NixOS with the IC development environment:
# Start nix-shell with IC tools
nix-shell https://github.com/ninegua/ic-nix/releases/latest/download/dfx-env.tar.gz
# Set PocketIC binary path
export POCKET_IC_BIN=$(which pocket-ic)
# Run tests
cargo test