Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nimdeveloper/better-duck/llms.txt
Use this file to discover all available pages before exploring further.
The Connection type is the primary entry point to better-duck. It wraps a RawConnection — which in turn owns a single DuckDB C-level connection — and exposes a fully safe, ergonomic API for opening databases, executing SQL, creating bulk appenders, and spawning sibling connections. Every FFI call is encapsulated behind the safe wrapper; no unsafe code ever surfaces in user code.
Opening a Connection
better-duck provides four constructor variants covering the two most common scenarios: in-memory (great for tests and one-shot scripts) and file-based (persistent on-disk databases), each with an optional Config argument for fine-grained DuckDB settings.
In-memory
File-based
With Config flags
In-memory with Config
use better_duck_core::connection::Connection;
let mut conn = Connection::open_in_memory()?;
Each call to open_in_memory() creates a completely independent in-memory database. If you need multiple connections that see the same data, use Database::open_in_memory() instead.use better_duck_core::connection::Connection;
let mut conn = Connection::open("my_database.duckdb")?;
The path is accepted as anything that implements AsRef<Path>. The file is created if it does not already exist.use better_duck_core::{connection::Connection, config::Config};
let config = Config::default().with("threads", "4")?;
let mut conn = Connection::open_with_flags("my_database.duckdb", config)?;
Config is a key-value store that maps directly to DuckDB’s configuration options (e.g. "threads", "memory_limit", "access_mode"). better-duck always injects "duckdb_api" = "rust" automatically regardless of what you pass.use better_duck_core::{connection::Connection, config::Config};
let config = Config::default().with("threads", "2")?;
let mut conn = Connection::open_in_memory_with_flags(config)?;
Method signatures
| Method | Description |
|---|
Connection::open_in_memory() -> Result<Connection> | Open a standalone in-memory database |
Connection::open_in_memory_with_flags(config: Config) -> Result<Connection> | Same, with extra DuckDB config |
Connection::open<P: AsRef<Path>>(path: P) -> Result<Connection> | Open (or create) an on-disk database |
Connection::open_with_flags<P: AsRef<Path>>(path: P, config: Config) -> Result<Connection> | Same, with extra DuckDB config |
All four constructors are annotated #[must_use = "connection should be used or explicitly dropped"]. Rust will warn you if the returned Connection is silently discarded — make sure to bind it to a variable or explicitly drop it when you are done.
Connection Lifecycle
A Connection is open from the moment it is created until either close() is called or it goes out of scope. The underlying DuckDB connection is destroyed in Drop, so resources are always reclaimed even if you never call close() explicitly.
use better_duck_core::connection::Connection;
fn main() -> better_duck_core::error::Result<()> {
let mut conn = Connection::open_in_memory()?;
assert!(conn.is_open());
// Explicit close — useful when you need to handle any close errors
conn.close()?;
assert!(!conn.is_open());
Ok(())
}
Lifecycle methods
| Method | Description |
|---|
conn.is_open() -> bool | Returns true if the underlying DuckDB connection handle is non-null |
conn.close() -> Result<()> | Closes the connection explicitly; also called automatically on drop |
close() is annotated #[must_use = "close result should be checked"]. Always propagate or handle the Result it returns; ignoring it silently discards any error that occurred during shutdown.
Cloning a Connection
try_clone() opens a second, independent connection to the same underlying database as cheaply as a single duckdb_connect FFI call — no file I/O. Both connections share the same data (including in-memory databases), and each can execute queries independently.
use better_duck_core::connection::Connection;
fn main() -> better_duck_core::error::Result<()> {
let mut a = Connection::open_in_memory()?;
a.execute_batch("CREATE TABLE t (id INTEGER)")?;
let mut b = a.try_clone()?;
b.execute_batch("INSERT INTO t VALUES (1)")?;
// `a` sees the row inserted by `b`
let mut result = a.execute("SELECT count(*) AS c FROM t")?;
let row = result.next().unwrap()?;
println!("{:?}", row.get("c")); // Some(BigInt(1))
Ok(())
}
| Method | Description |
|---|
conn.try_clone() -> Result<Connection> | Open a second connection to the same database |
Accessing the Database Handle
Every Connection keeps an Arc-backed reference to its underlying Database. Calling conn.database() returns that handle as a cheap Arc bump — no new DuckDB objects are created.
Once you have the Database handle, you can call db.connect() to spawn as many additional connections as you need, all sharing the same underlying data.
use better_duck_core::connection::Connection;
fn main() -> better_duck_core::error::Result<()> {
let conn = Connection::open_in_memory()?;
// Retrieve the shared database handle
let db = conn.database();
// Spawn further connections from it
let mut sibling_a = db.connect()?;
let mut sibling_b = db.connect()?;
sibling_a.execute_batch("CREATE TABLE t (id INTEGER)")?;
sibling_b.execute_batch("INSERT INTO t VALUES (42)")?;
// The original `conn`, `sibling_a`, and `sibling_b` all see the same data
Ok(())
}
| Method | Description |
|---|
conn.database() -> Database | Return the Arc-backed Database handle for this connection |
For full details on the Database type, shared in-memory databases, and thread safety, see the Database Sharing guide.
Execute Methods
Once you have a connection you can run SQL immediately. The three core execute methods are:
| Method | Best for |
|---|
conn.execute_batch(sql) -> Result<()> | DDL and fire-and-forget DML; result discarded |
conn.execute(sql) -> Result<DuckResult> | Any statement where you want rows or change counts |
conn.execute_with(sql, &mut [...]) -> Result<DuckResult> | Parameterized queries with positional $1, $2, … binds |
See the Querying guide for full examples and row-reading patterns.