Introduction
Build a type-safe ClickHouse analytics backend with a TypeScript query builder, semantic datasets, multi-tenant APIs, React hooks, and MCP tools.
A TypeScript semantic layer for ClickHouse
hypequery gives TypeScript teams one place to define ClickHouse analytics and reuse them in backend code, HTTP APIs, React dashboards, and MCP tools for AI agents. It is an open-source library that runs inside your application—not a hosted BI platform or a separate semantic-layer server.
Start with the type-safe query builder when a query is local. Add a dataset when dimensions, measures, metrics, tenant isolation, and time grains become shared product meaning. Add Serve, React, or MCP only when another consumer needs the same contract.
The canonical dataset
import { dataset, dimension, measure } from '@hypequery/datasets';
export const Orders = dataset('orders', {
source: 'orders',
tenantKey: 'tenant_id',
timeKey: 'created_at',
dimensions: {
id: dimension.string(),
region: dimension.string(),
status: dimension.string(),
createdAt: dimension.timestamp({ column: 'created_at' }),
},
measures: {
revenue: measure.sum('amount'),
orderCount: measure.count('id'),
p95OrderValue: measure.percentile('amount', 0.95),
latestStatus: measure.argMax('status', 'createdAt'),
},
});
export const revenue = Orders.metric('revenue', {
measure: 'revenue',
label: 'Revenue',
});
This is ordinary TypeScript: review it in a pull request, test it in CI, import it in a worker, serve it as an API, consume it with a typed React hook, or expose it as a bounded MCP tool.
The packages
| Package | Role |
|---|---|
@hypequery/clickhouse | Schema generation and a type-safe ClickHouse query builder |
@hypequery/datasets | Code-first semantic datasets, dimensions, measures, metrics, relationships, time grains, and tenant rules |
@hypequery/serve | Validated HTTP APIs, OpenAPI, authentication, tenancy, and runtime adapters |
@hypequery/react | Typed TanStack Query hooks for queries, metrics, and datasets |
@hypequery/mcp | Governed ClickHouse tools for Claude, Cursor, and other MCP clients |
@hypequery/cli | Scaffolding, schema generation, local docs, and deployment commands |
Choose your starting point
- Run
npx hypequery initif you want a working project scaffold. - Use
@hypequery/clickhousedirectly if you only need typed local queries. - Define
@hypequery/datasetswhen analytics meaning is shared. - Add
serve(), React hooks, or MCP when the model needs to cross a process boundary.
Connect ClickHouse, generate types, and run the first query
Check the shipped builder, aggregate, dataset, React, and MCP surface
Write typed ClickHouse filters, joins, aggregations, and native clauses
Browse complete framework and dashboard examples
The progression in code
A query can begin as a local builder chain:
const revenueByRegion = await db
.table('orders')
.select(['region'])
.sum('amount', 'revenue')
.groupBy('region')
.execute();
When it becomes reusable business meaning, promote it into the Orders dataset. When it needs an HTTP contract, pass the metric and dataset to Serve:
const { serve } = initServe({
context: () => ({ db }),
});
export const api = serve({
queryBuilder: db,
metrics: { revenue },
datasets: { orders: Orders },
});
That API can be called in process, over HTTP, from @hypequery/react, or through @hypequery/mcp. The definition and tenant rules stay in one place.