Quarry

Add to an existing project

Reuse your ClickHouse client and introduce Quarry one query at a time.

Use Quarry 0.10.0 with your existing ClickHouse connection. If you are upgrading from 0.9.x, review the migration notes for INSERT SELECT, join settings, and the Node 22 minimum.

Install

Terminal
npm install quarry@0.10.0 @clickhouse/client

Use an ESM TypeScript project. Reuse the @clickhouse/client connection your application already owns, or create one with your existing connection options.

For Node projects, install @types/node if it is not already present. Your TypeScript configuration should include Node types and the disposable-symbol library used by the client. Merge these settings into your existing config:

JSON
{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "target": "ES2022",
    "lib": ["ES2022", "ESNext.Disposable"],
    "types": ["node"],
    "strict": true
  }
}

A bundler-based application can retain its existing Bundler module resolution.

Describe one table and query it

The table below is an example shape. Replace its names and types with your actual schema. Plain interfaces are useful for a first query; use introspection for a larger schema.

TypeScript
import {  } from "@clickhouse/client";
import { , type  } from "quarry";

interface DB {
  : {
    : ;
    : string;
  };
}

const  = ({
  : ..,
  : ..,
  : ..,
  : ..,
});
const  = <DB>({  });

const  = .("events")
  .("user_id", "event_type")
  .("event_type", "=", "signup")
  .(20);

const rows = await .();
const rows: {
    user_id: string;
    event_type: string;
}[]

rows is { user_id: string; event_type: string }[]. Selecting another column changes that result type. A misspelled column produces a type error.

Keep your existing client lifecycle: close a shared client at application shutdown, or call await client.close() at the end of a standalone script.

Inspect before replacing an existing query

TypeScript
const { query: sql, params } = query.toSQL();

Compare that SQL and the returned rows with your current implementation. Introduce Quarry at one query boundary, keeping the rest of your code as it is. The comparison example shows this process for a complete analytics endpoint.

Grow the schema when you need it

Generate your DB types from ClickHouse and import them into your application. For services using only part of a large schema, derive Pick<DB, ...> from the shared generated type.

For a complete local database and runnable recipes using the same version, continue with the quickstart.

On this page