Which crate?
better-duck-core
The driver. Row-oriented access, prepared statements, the bulk appender, columnar / zero-copy
reads, UDFs, async, pooling, and Arrow / Polars interop.
better-duck-diesel
The Diesel 2.3 ORM backend. A typed query builder (DSL), migrations, an r2d2 pool, and a large
library of DuckDB-specific SQL functions, aggregates, and operators.
better-duck-tauri
A Tauri v2 plugin that exposes embedded DuckDB to a webview as a local-analytics engine, with
JS/TS bindings (
core or diesel backend).Which API for which task?
Even within the core crate, picking the right tool matters:- One value per row in a
SELECT/WHERE→ a scalar UDF (#[duckdb_scalar]). - Rows from a
FROM→ a table UDF (#[duckdb_table_function]). - Bulk insert → the
Appender, not row-by-rowINSERT. - Read a big result fast / zero-copy columns → the
columnarfeature’sfetch_chunk+as_slice. - ORM / typed query builder →
better-duck-diesel. - Desktop app (webview) database →
better-duck-tauri. - Map a Rust struct to a row →
#[derive(FromRow)]/#[derive(ToRow)].
Core directly, or the Diesel backend?
Use better-duck-core directly when…
Use better-duck-core directly when…
- You want the leanest dependency tree and direct, row-oriented control.
- You need the bulk
Appender, columnar / zero-copy reads, or Arrow / Polars export. - You’re registering user-defined functions, or driving the engine from inside a Tauri plugin or an embedded context.
- You prefer writing SQL yourself over a query-builder DSL.
Use better-duck-diesel when…
Use better-duck-diesel when…
- You want a typed query builder, compile-time-checked schema (
table!), and migrations. - You want DuckDB-specific SQL exposed as Diesel DSL (
.ilike(),string_agg,date_trunc,ST_*, …) that stock Diesel doesn’t ship. - You want r2d2 pooling with a
SharedDuckDbConnectionManagerand a per-connectionon_connecthook (the place to register UDFs orLOADextensions on every pooled connection). - It builds on
better-duck-core, so you can still drop to the core connection for raw SQL, the appender, or UDF registration when you need to.
Use better-duck-tauri when…
Use better-duck-tauri when…
- You’re building a Tauri v2 desktop app and want DuckDB available in the webview as a local
analytics engine, with ready-made JS/TS bindings — backed by either the
coreor thedieselbackend.
Feature-flag decision tree
Start from the defaults (chrono, decimal) and add what your task needs. See
Feature flags for the full table.
1
Dates & numbers
Keep
chrono (default) for chrono temporals, or disable defaults for the native date/time
structs. Keep decimal (default) for rust_decimal. Add time to bind time-crate values, or
uuid for uuid::Uuid interop.2
Extensions
Need JSON, Parquet, or ICU with no runtime install? Bundle them with
json / parquet / icu.
(Spatial loads at runtime via the extension API, no feature needed in core.)3
Data interop
Exporting to Arrow?
arrow. Want Polars DataFrames? polars (implies arrow). Need fast /
zero-copy columnar reads? columnar.4
Functions & mapping
Registering Rust functions as DuckDB functions?
udf (also enables the columnar APIs). Mapping
structs/enums, or want transaction! / params!? derive.5
Concurrency
Async application?
async. Connection pooling? pool. Both together give you AsyncPool.Related
Feature flags
Every core feature explained.
Architecture
The crate family and the FFI safety model.
Quickstart
A minimal end-to-end example with the core crate.
DuckDB essentials
The dialect, types, and extensions through the driver.