Skip to content

Embedded SDK

Dynoxide can be used as a Rust library, calling the database directly without any HTTP overhead. This is the fastest mode - single-digit microsecond operations, no serialisation, no network stack.

It also runs on platforms where DynamoDB Local can't, including iOS.

Add the dependency

[dependencies]
dynoxide-rs = { version = "0.12", default-features = false, features = ["native-sqlite"] }

For encrypted storage:

[dependencies]
dynoxide-rs = { version = "0.12", default-features = false, features = ["encryption"] }

Basic usage

use dynoxide::Database;

// In-memory database (great for tests)
let db = Database::memory().unwrap();

// Persistent, file-backed
let db = Database::new("data.db").unwrap();

// Encrypted (requires the encryption feature)
let db = Database::new_encrypted("data.db", "your-hex-key").unwrap();

The API uses DynamoDB-compatible request and response types as serde_json::Value. You call the same operations you'd call over HTTP - CreateTable, PutItem, Query, and so on - but without the network round trip.

Upgrading to 0.12

0.12.0 is source-breaking for library consumers, and only for them. The DynamoDB wire API and the CLI, server and MCP surfaces are unchanged, so if you run the binary rather than depend on the crate, nothing here affects you.

Two public types gained a field and became #[non_exhaustive]:

  • partiql::parser::Statement - the Update and Delete variants carry a returning field, which is what makes the new PartiQL RETURNING support work.
  • actions::batch_execute_statement::BatchStatementResponse - gained a table_name field.

Code that constructs either type, or matches it exhaustively, needs a .. rest pattern. That's the whole migration.

Feature flags

Flag Default Description
native-sqlite Yes Bundles plain SQLite. No OpenSSL.
http-server Yes The axum HTTP server exposing the DynamoDB JSON API.
mcp-server Yes The MCP server, over stdio and Streamable HTTP.
import Yes The dynoxide import CLI, with anonymisation.
encryption No SQLCipher with vendored OpenSSL. Adds Database::new_encrypted().
encryption-cc No As encryption, but Apple CommonCrypto instead of OpenSSL. For macOS and iOS.
encrypted-full No encryption plus the server, MCP and import features.
wasm-sqlite No The wasm32 browser backend, a preview. Pulls in neither native SQLite nor the CLI.

native-sqlite and encryption are mutually exclusive - they select different SQLite backends.

This one bites in workspaces. Cargo unifies features across a workspace, so if one crate depends on dynoxide-rs with default features (picking up native-sqlite) and another asks for encryption, both activate and the build fails with a backend conflict. Set default-features = false on every dynoxide-rs dependency in the workspace.

Testing

The embedded mode is particularly useful for integration tests. Each test can spin up its own Database::memory() instance - there's no shared state, no port conflicts, and startup is roughly 0.2ms.

#[test]
fn test_user_creation() {
    let db = Database::memory().unwrap();
    // create table, put item, assert...
}

No test fixtures, no cleanup, no waiting for a server to start. The database exists for the lifetime of the Database value and is dropped automatically.

For the full API surface and feature flags, see the crate documentation on crates.io.