Skip to main content

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.

better-duck is a Rust library that gives you direct, safe access to DuckDB — the embeddable analytical database — without requiring Arrow, a system-installed DuckDB library, or an ORM. The optional better-duck-diesel crate adds a full Diesel 2.3 backend so your existing query DSL code works unchanged.

Quickstart

Open a database, execute queries, and iterate rows in under five minutes.

Installation

Add better-duck-core and better-duck-diesel to your Cargo.toml with the right features.

Core Library

Connections, prepared statements, the bulk appender, async facade, and connection pooling.

Diesel ORM Backend

Use Diesel 2.3 DSL — INSERT, SELECT, UPDATE, DELETE, transactions — against DuckDB.

Type System

Full DuckDB type coverage: primitives, composite types, UUID, BIT, BIGNUM, and more.

User-Defined Functions

Register plain Rust functions as DuckDB scalar or table functions via proc-macro attributes.

Why better-duck?

Most Rust DuckDB bindings depend on Arrow or require a system-installed DuckDB library. better-duck takes a different approach:

Bundled DuckDB

Ships with the DuckDB C library compiled in — no system package or runtime download needed.

No Arrow Overhead

Row-oriented API skips Arrow entirely. Great for app-level OLAP where you just need rows.

Diesel ORM

Full Diesel 2.3 backend — your existing table! / query DSL code works without changes.

Embedded-First

Designed for Tauri desktop apps, iOS cross-builds, and environments without system libraries.

Safe Public API

Every FFI call is wrapped. Nothing unsafe leaks into user code.

User-Defined Functions

#[duckdb_scalar] and #[duckdb_table_function] register Rust fns as DuckDB functions with no unsafe code.

Crates at a Glance

CrateDescription
better-duck-coreLow-level DuckDB wrapper — connections, prepared statements, bulk appender, full type coverage
better-duck-dieselDiesel 2.3 backend — full query DSL, migrations, r2d2 connection pool
better-duck-macrosProc-macro crate for #[duckdb_scalar] and #[duckdb_table_function] (used via better-duck-core)
better-duck-sysVendored DuckDB C library and FFI bindings (used internally)
better-duck is currently in beta (v0.1.0-beta.4). The API is settling — breaking changes before 1.0 are possible. Check the changelog before upgrading.

Quick Example

use better_duck_core::connection::Connection;
use better_duck_core::types::value::DuckValue;

fn main() -> better_duck_core::error::Result<()> {
    let mut conn = Connection::open_in_memory()?;

    conn.execute_batch(
        "CREATE TABLE events (id INTEGER, label TEXT, score DOUBLE);
         INSERT INTO events VALUES (1, 'alpha', 9.5), (2, 'beta', 7.2);",
    )?;

    let mut result = conn.execute("SELECT id, label, score FROM events ORDER BY id")?;
    for row in result {
        let row = row?;
        println!(
            "id={:?}  label={:?}  score={:?}",
            row.get("id"),
            row.get("label"),
            row.get("score"),
        );
    }
    Ok(())
}
1

Add the dependency

Add better-duck-core to your Cargo.toml. See Installation for the full setup.
2

Open a connection

Use Connection::open_in_memory() for tests or Connection::open("path/to/db.duckdb") for persistent storage.
3

Execute SQL

Use execute_batch for DDL and simple DML, and execute (or execute_with for parameterized queries) for statements that return rows.
4

Iterate rows

execute returns a DuckResult that implements Iterator<Item = Result<DuckRow>>. Each DuckRow exposes column values as DuckValue variants.

Build docs developers (and LLMs) love