Getting started
Install Quarry, define a typed database, and run your first query.
Install
pnpm add @oorestisime/quarry @clickhouse/clientQuarry expects you to bring your own @clickhouse/client instance.
Create a typed DB
Define your schema as a plain TypeScript interface and pass it as the type
parameter to createClickHouseDB.
import { createClient } from "@clickhouse/client";
import { createClickHouseDB } from "@oorestisime/quarry";
interface DB {
event_logs: {
user_id: number;
event_type: string;
created_at: string;
event_date: string;
properties: string;
};
users: {
id: number;
email: string;
status: string;
};
}
const client = createClient({
url: "http://localhost:8123",
});
const db = createClickHouseDB<DB>({ client });That db value is now strongly typed against your schema. Table names,
columns, and aliases are all checked.
Why Quarry stays plain
Quarry is a query builder, not a schema modeling framework. The library stays honest by typing what your queries read and write under ClickHouse's runtime semantics instead of asking you to maintain a second schema DSL.
If you already have a database and want help bootstrapping the DB type, use
the CLI introspection flow instead of writing the interface by hand.
npx @oorestisime/quarry-cli introspect --database analytics --out db.tsFor repeatable generation, the introspection guide shows how to keep connection
options, filters, and column type overrides in quarry.introspect.json.
Build and execute a query
import { } from "@clickhouse/client";
import { } from "@oorestisime/quarry";
interface DB {
: {
: number;
: string;
: string;
};
}
const = <DB>({
: ({ : "http://localhost:8123" }),
});
const = await
.("event_logs as e")
.("e.user_id", "e.event_type")
.("e.event_type", "=", "signup")
.("e.created_at", "desc")
.(50)
.execute();Hover over rows above — the result type is inferred from the columns
you actually selected.
Inspect SQL without executing
When you want to see what Quarry is going to send to ClickHouse without
running it, use toSQL() instead of execute().
const compiled = db
.selectFrom("event_logs as e")
.select("e.user_id", "e.event_type")
.where("e.event_type", "=", "signup")
.toSQL();
compiled.query;
// SELECT e.user_id, e.event_type
// FROM event_logs AS e
// WHERE e.event_type = {p0:String}
compiled.params;
// { p0: "signup" }The placeholders are ClickHouse parameterised queries, not string interpolation. The driver substitutes them server-side.