Skip to content

Repository files navigation

npm version

chdb-node

chDB Node.js bindings — an in-process ClickHouse engine for Node, Bun and Deno.

v3 (Layer 1) is in development. The v2 query / queryBind / Session API is preserved (your v2 code keeps working); v3 adds async queries, server-side parameter binding, inserts, streaming, and Arrow output.

Install

npm i chdb

Prebuilt native binaries ship as per-platform subpackages (@chdb/lib-*, resolved via optionalDependencies) — no local compilation, no node-gyp, no Python. First-batch platforms: Linux x64/arm64 (glibc) and macOS x64/arm64. Windows is not supported (use WSL2).

Usage

const{ query, queryAsync, insert, Session }=require("chdb");// or: import { ... } from "chdb"// Sync standalone query (v2-compatible, returns a string)console.log(query("SELECT version(), 'Hello chDB'","CSV"));// Async query (non-blocking) -> ChdbResult (text() / json() / bytes() + metrics)constr=awaitqueryAsync("SELECT number FROM numbers(5)",{format: "JSONEachRow"});console.log(r.rowsRead,r.elapsed);// Server-side parameter binding (no SQL injection surface)const{ queryBind }=require("chdb");console.log(queryBind("SELECT {n:UInt32} * 2 AS v",{n: 21},"CSV"));// 42// Session: persistent/in-memory databaseconstsession=newSession();// temp dir; or new Session("./data")session.query("CREATE TABLE t (id UInt32, name String) ENGINE = MergeTree() ORDER BY id");// Insert (inline, async; never reads stdin)awaitsession.insert({table: "t",values: [{id: 1,name: "Alice"},{id: 2,name: "Bob"}]});// Streaming (chunk-by-chunk, no full buffering)forawait(constrowofsession.queryStream("SELECT * FROM t").rows()){console.log(row);}// Arrow output (no serialization on your side)consta=awaitsession.queryAsync("SELECT * FROM t",{format: "arrow"});consttable=a.toArrow();// requires the optional `apache-arrow` peer dep// const bytes = a.bytes(); // raw Arrow IPC if you bring your own Arrowsession.close();// (cleanup() is an alias; `using` is supported too)

Errors are typed (ChdbSyntaxError, ChdbQueryError, ChdbConnectionError, ChdbBindError, ChdbInsertError, ChdbStreamError, ChdbArrowError, ChdbAbortError, ChdbTimeoutError, …), each carrying .code, the ClickHouse .clickhouseCode, and .cause.

One data directory at a time

libchdb binds a single data directory per process, so opening a Session takes the slot the stateless query/queryAsync calls were using and closes their connection. A connection closed while an operation is still running on it aborts the engine for the rest of the process, so new Session() refuses instead:

const{ queryAsync, Session, drainPending }=require("chdb");constp=queryAsync("SELECT max(sipHash64(number)) FROM numbers(20000000)");newSession();// throws: 1 standalone operation is still runningawaitp;newSession();// fine

Awaiting your own promise is not always enough. An aborted or timed-out call rejects immediately while the engine keeps computing, and close() returns before the connection is really gone when an operation is still using it. drainPending() waits for both:

constac=newAbortController();constp=queryAsync("SELECT max(sipHash64(number)) FROM numbers(20000000)",{signal: ac.signal,});ac.abort();awaitp.catch(()=>{});// rejected, but the engine is still computingawaitdrainPending();// now the connection is actually freeconsts=newSession("./data");

Moving between directories works the same way: after session.close(), wait with drainPending() before opening one at a different path. Opening another session at the same path needs no wait — those connections coexist by design.

Behaviour change. Earlier versions did not refuse — they closed the busy connection, which usually aborted the engine and on macOS could leave a query whose promise never settled. Code that opened a session without awaiting its standalone queries now gets an error at the call site instead of a failure somewhere later.

Feature matrix

CapabilityStatus
Stateless query (sync + async)
Session (persistent / in-memory)
Server-side parameter binding ({name:Type})
Insert (object / positional rows)
Streaming results (AsyncIterable)
Arrow output (format: 'arrow' + toArrow())
AbortSignal / timeout✅ (single-shot is honest: rejects early; native runs to completion)
Arrow scan (registerArrowTable, Arrow input)⏳ follow-up
Arrow zero-copy (M2, { zeroCopy: true })⏳ follow-up
chDB ↔ @clickhouse/client integration (chdb/connection, experimental)

chDB ↔ @clickhouse/client integration (chdb/connection, experimental)

Status: this integration uses the experimental createClient({ connection }) hook in @clickhouse/client (clickhouse-js#879 merged; framing follow-up #880 merged). Upstream considers this a deliberately narrow chDB-only hook — not a public plugin system — and the shape may change. We'll keep chdb/connection working against whatever the upstream hook evolves into.

For users coming from @clickhouse/client, chdb-node ships a Connection implementation under the chdb/connection subpath that plugs into @clickhouse/client's createClient({ connection }) injection point (tracking issue: clickhouse-js#865).

import{createChdbConnection}from'chdb/connection'constconn=createChdbConnection({path: ':memory:'})constr=awaitconn.query({query: 'SELECT * FROM numbers(5)',format: 'JSONEachRow'})letbody=''forawait(constchunkofr.stream)body+=Buffer.from(chunk).toString('utf8')console.log(JSON.parse(`[${body.trim().split('\n').join(',')}]`))awaitconn.close()// chDB-specific escape hatches (raw ChdbResult, raw insert, session info)conn.chdb.queryAsync('SELECT 1',{format: 'arrow'})// bytes/text/json/toArrowconn.chdb.session.path// bound on-disk path

See docs/design/pluggable-connection.md for the full design, the Connection interface, the .chdb extension namespace, the tests/clickhouse-js/skip_list.json parity blacklist, and the sync policy with @clickhouse/client.

Design docs

Runtimes

A single N-API binary serves Node 18/20/22 + Bun + Deno.

Develop / build from source

npm install # JS deps only (no compile-on-install)
npm run libchdb # download libchdb for this platform
npm run build # node-gyp build + fix loader path + tsc (dist)
npm run test:all # v2 (mocha) + v3 (vitest)
npm run build:platform # package this platform's @chdb/lib-* subpackage

About

Native NodeJS bindings for chDB, an in-process SQL OLAP Engine powered by ClickHouse

Topics

Resources

Stars

57 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages