⭐ Please star hypequery if it is useful to you 🙏
It helps more people find the project.
- Build on top of your real ClickHouse schema instead of hand-maintained query types
- Reuse the same query definition across scripts, APIs, React apps, and agents
- Start local with the query builder, then add HTTP routes only when you need them
- Keep inputs, outputs, and SQL behavior explicit enough to test and reason about
@hypequery/clickhouse: typed ClickHouse query builder@hypequery/serve: code-first runtime for query contracts, HTTP routes, docs, and adapters@hypequery/react: thin TanStack Query hooks for hypequery APIs@hypequery/cli: scaffolding, schema generation, and local dev tooling
npm install -D @hypequery/cli
npx hypequery initThat gives you the main path:
- Generate schema types from ClickHouse
- Write typed queries locally
- Expose the queries over HTTP when you need a shared contract
import { createQueryBuilder } from '@hypequery/clickhouse';
import type { IntrospectedSchema } from './analytics/schema.js';
const db = createQueryBuilder<IntrospectedSchema>({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD ?? '',
database: process.env.CLICKHOUSE_DATABASE!,
});
const revenueByRegion = await db
.table('orders')
.select(['region'])
.where('created_at', 'gte', '2026-01-01')
.sum('total', 'revenue')
.groupBy('region')
.orderBy('revenue', 'DESC')
.execute();import { initServe } from '@hypequery/serve';
import { z } from 'zod';
import { db } from './analytics/client.js';
const { query, serve } = initServe({
context: () => ({ db }),
basePath: '/api/analytics',
});
const activeUsers = query({
description: 'List active users by region',
input: z.object({ region: z.string() }),
query: ({ ctx, input }) =>
ctx.db
.table('users')
.where('status', 'eq', 'active')
.where('region', 'eq', input.region)
.execute(),
});
export const api = serve({
queries: { activeUsers },
});
api.route('/activeUsers', api.queries.activeUsers);The same query can then be:
- executed directly with
api.execute(...) - exposed as an HTTP route
- consumed from React with
@hypequery/react - described for tools and agents
If you do not need serve, a standalone query can execute itself:
const activeUsers = query({
input: z.object({ region: z.string() }),
query: ({ input }) =>
db
.table('users')
.where('status', 'eq', 'active')
.where('region', 'eq', input.region)
.execute(),
});
await activeUsers.execute({
input: { region: 'EMEA' },
});The same served execution API also works for semantic metrics:
import { createAPI } from '@hypequery/serve';
import { dataset, dimension, measure } from '@hypequery/datasets';
const Orders = dataset('orders', {
source: 'orders',
dimensions: {
region: dimension.string(),
},
measures: {
revenue: measure.sum('total'),
},
});
const revenue = Orders.metric('revenue', { measure: 'revenue' });
export const api = createAPI({
queryBuilder,
metrics: { revenue },
datasets: { orders: Orders },
});
await api.execute('revenue', {
input: { dimensions: ['region'] },
});
await api.execute('dataset:orders', {
input: { dimensions: ['region'], measures: ['revenue'] },
});# Scaffold analytics files and env vars
npx hypequery init
# Run the local dev server with docs
npx hypequery dev
# Regenerate schema types
npx hypequery generateApache-2.0. See LICENSE.
