Run a SQL query against DuckDB and convert the result (an Arrow table) to JSON-stat via the Arrow hub. On export, register an Arrow-backed view or table into a DuckDB connection. Two optional peer backends are supported:
- Node:
duckdb-async— native binding. - Browser:
@duckdb/duckdb-wasm— WASM.
# Node
npm install jsonstat-io duckdb-async
# Browser
npm install jsonstat-io @duckdb/duckdb-wasmBoth are optional peer dependencies. The DuckDB adapter is not available via the CLI for import — it requires a live connection, so use the programmatic API.
import { duckdbToCube, duckdbToDataset, openDuckdbNode } from "jsonstat-io/duckdb";import { duckdbToDataset, openDuckdbNode } from "jsonstat-io/duckdb";
const conn = await openDuckdbNode("./warehouse.duckdb");
const dataset = await duckdbToDataset(conn,
"SELECT year, country, amount FROM sales WHERE year >= 2020",
{ roles: { time: ["year"], geo: ["country"] } }
);openDuckdbNode(path?) is a convenience helper that lazily imports duckdb-async and returns a DuckdbConnection. Pass ":memory:" (the default) for an in-memory database.
Set up @duckdb/duckdb-wasm yourself, then pass the connection:
import * as duckdb from "@duckdb/duckdb-wasm";
import { duckdbToDataset } from "jsonstat-io/duckdb";
// Standard duckdb-wasm bootstrap (see their docs for full setup)
const bundle = await duckdb.selectBundle(duckdb.getBundle());
const worker = new Worker(bundle.mainWorker);
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
const conn = await db.connect();
const dataset = await duckdbToDataset(conn, "SELECT * FROM sales");The adapter normalizes both duckdb-async and duckdb-wasm connection shapes — it looks for .arrow() or .arrowResult() methods.
For lazy browser setups, pass a connect factory instead of a live connection. The adapter opens and closes it automatically:
const obs = await duckdbToCube(null, query, {
connect: async () => await createConnection(),
});cubeToDuckdb converts the Observations IR to an Arrow table (via cubeToArrow) and registers it as a named view or table in the DuckDB connection.
import { exportDataset } from "jsonstat-io";
// JSON-stat → Arrow-backed view in DuckDB
const table = await exportDataset(dataset, {
to: "duckdb",
connection: conn,
tableName: "sales",
mode: "view", // or "table" to materialize
});Or use cubeToDuckdb directly on the IR:
import { cubeToDuckdb } from "jsonstat-io/duckdb";
await cubeToDuckdb(conn, obs, { tableName: "sales", mode: "view" });
// Now queryable in DuckDB:
const result = await conn.arrow("SELECT * FROM sales WHERE amount > 100");| Mode | Behavior |
|---|---|
"view" (default) |
Registers a temporary Arrow-backed view — fast, zero-copy, lives for the connection's lifetime. |
"table" |
Materializes the Arrow data into a persistent DuckDB table via CREATE TABLE ... AS SELECT * FROM <view>. |
Extends ArrowToCubeOptions:
| Field | Type | Description |
|---|---|---|
connect |
() => Promise<DuckdbConnection> |
Factory that returns a fresh connection (browser). Takes precedence over connection, closed after use. |
measure |
string |
Measure column name |
dimensions |
string[] |
Dimension column names |
roles |
RoleMap |
Role assignments |
| Field | Type | Default | Description |
|---|---|---|---|
tableName |
string |
"data" |
Name of the view/table to create. |
mode |
"view" | "table" |
"view" |
Register a zero-copy view or materialize a table. |
interface DuckdbConnection {
arrow(query: string): Promise<any>; // Returns an Apache Arrow Table
close?(): Promise<void>;
// Export-side (registered by cubeToDuckdb):
register?(name: string, table: any): Promise<void>;
insert_arrow_table?(table: any, name: string): Promise<void>;
run?(sql: string): Promise<void>;
}Both duckdb-async and @duckdb/duckdb-wasm connections satisfy this interface. If your connection uses arrowResult() instead of arrow(), the adapter handles that too.
Import:
duckdbToCuberuns the SQL query viaconn.arrow(query)(orarrowResult()).- Gets back an Apache Arrow
Table. - Passes it to
arrowToCube→ObservationsIR.
Export:
cubeToDuckdbcallscubeToArrow(obs)→ Apache ArrowTable.- Registers the table into the connection via
conn.register(name, table)(orinsert_arrow_table). - For
mode: "table", runsCREATE TABLE <name> AS SELECT * FROM <tmpview>.
If neither DuckDB package is installed, a DuckdbSourceError is thrown.