Quickstart
Install the package, open a database with a connection string, and run SQL in-process — no server, no Rust, no infrastructure. The prebuilt engine ships with the npm package, so you go from bun add to a durable query in two steps. The same connection string later moves you to object storage or a Postgres-wire server with no code change.
Prerequisites
- MUST Bun ≥ 1.1 — the embedded client runs on
bun:ffi. Install from bun.sh. - MAY A Rust toolchain (1.80+) only if you build from source or are on a platform without a prebuilt binary — see Build from source. The npm path needs nothing.
- MAY An S3-compatible bucket (AWS S3 / Cloudflare R2 / MinIO) only for the disaggregated (
s3://) backend; thefile://path needs nothing.
1 · Install
Add the wrapper to your project. The native engine ships as a set of per-platform binary packages (@twilldb/engine-darwin-arm64, @twilldb/engine-linux-x64, …) declared as optional dependencies; your package manager downloads only the one matching your OS and CPU. There is no build step, no postinstall, and no Rust toolchain.
bun add @twilldb/bun
Adding to a real app?
This page is the fast path to a running query. To wire Twill DB into a new or existing project — a reusable db module, env-driven connection string, .gitignore — or to hand the setup to an AI coding agent, see Add to a project.
2 · Your first embedded database
Open a file:// database and run SQL at function-call latency. open(url) returns a handle; the using declaration disposes it at scope exit. Values come back as strings (a SQL NULL as null) — cast on the application side if you need numbers.
// notes.ts
import { open } from "@twilldb/bun";
using db = open("file://./local.db"); // pure-embedded, zero network
db.exec(`CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT)`);
db.query("INSERT INTO notes VALUES (?, ?)", [1, "hello"]); // parameterized writes go through query()
const rows = db.query("SELECT id, body FROM notes");
console.log(rows); // [{ id: "1", body: "hello" }]
bun run notes.ts
The database persists to ./local.db and is rebuilt by replaying the durable WAL on reopen, so your data survives a process restart. Use ? placeholders with a value array for any user input — values never touch the SQL text. See Embedded (bun:ffi) for the full API: prepared statements, transactions, and resource management.
3 · Go disaggregated (optional)
To make the same code path disaggregated and scale-to-zero, change only the connection string — no recompile, no code change.
using db = open("s3://my-bucket/notes"); // disaggregated; durable on object storage
Provide bucket credentials through the environment (never in code). See Connect to your database for the supported schemes and Scale-to-zero for the lifecycle.
4 · Start a server (optional)
Prefer a wire protocol over the in-process library? Run the same engine behind a Postgres-wire listener and connect with any Postgres client (cleartext / sslmode=disable). This path is built from the repo with Cargo.
cargo run -p twill-server -- --listen 127.0.0.1:5433 --db file://./srv.db # or s3://bucket/db
psql "host=127.0.0.1 port=5433 user=postgres sslmode=disable"
// Or from Bun's built-in client:
import { SQL } from "bun";
const sql = new SQL("postgres://[email protected]:5433/srv?sslmode=disable");
const rows = await sql`select 1 as n`;
See Postgres client and the per-language guides under Connect as server for Node, Python, Go, Drizzle, Prisma, and PostgREST.
5 · Branch your data
A branch is an instant copy-on-write fork: it sees the base's data and writes in isolation. Creating one copies no pages.
using preview = db.branch("preview");
preview.exec("INSERT INTO notes VALUES (2, 'branch-only')");
// the base never sees the branch's write, and vice versa
Build from source
You only need this if you are contributing to the engine, running on a platform without a prebuilt binary, or testing an unreleased change. Building produces the static/dynamic libraries plus the frozen C ABI header; the wrapper's loader then picks the library up from the cargo target dir automatically (or set TWILLDB_ENGINE_PATH to an explicit path).
git clone https://github.com/bihaviour/twill-db
cd twill-db
cargo build -p twill-engine --release
# → target/release/libengine.{a,so,dylib} + crates/engine/include/engine.h
cd clients/bun && bun install && bun test # end-to-end embedded test suite
Rebuild after engine or ABI changes
The wrapper loads the native library through bun:ffi and pins an expected ABI version. After any change that touches the C ABI or engine behaviour, rebuild the release libengine before running the client — otherwise it runs against a stale binary.