TypeScript + ClickHouse · Pre-1.0

Your analytics queries.
Now with types.

Keep your ClickHouse client. Compose filters, joins, and aggregations in TypeScript, see the SQL you send, and infer the rows you receive.

A seeded database, generated types, and a working endpoint.

Write a query
const query = db.selectFrom("events")
  .selectExpr(eb => [
    "event_type",
    eb.fn.count().as("events"),
  ])
  .where("tenant_id", "=", tenantId)
  .groupBy("event_type");

const rows = await query.execute();
Know what comes back
// Inferred result
{ event_type: string; events: string }[]

Counts are strings because ClickHouse returns UInt64 that way in JSON.

01

Use your existing connection

Quarry works with @clickhouse/client and plain TypeScript schema types.

02

Keep SQL visible

Inspect generated SQL and bound parameters before executing a query.

03

Build on ClickHouse

Use PREWHERE, FINAL, arrays, dictionaries, and LIMIT BY directly.

Learn by querying

Start with a result you need.

All recipes

Already using ClickHouse?

Keep your connection and replace one query at a time. Generate types from your database as your use grows.

Add Quarry to your project →

Deciding whether it fits?

Compare a real endpoint built with raw SQL, Quarry, Kysely, and hypequery. See the tradeoffs and supported clauses.

Compare approaches →

Have a query that is difficult to compose or type?

Bring a real query →