Quick Start
Get started with hypequery.
Choose your route
This page has two paths:
- Route 1: Query Builder first if you want typed ClickHouse queries running in-process as quickly as possible
- Route 2: Reusable queries and serve runtime if you want named query contracts, HTTP routes, docs, or OpenAPI
Route 1: Query Builder first
If you only need typed ClickHouse queries in your application code, start here:
Install the packages
npm install @hypequery/clickhouse
npm install -D @hypequery/clipnpm add @hypequery/clickhouse
pnpm add -D @hypequery/cliyarn add @hypequery/clickhouse
yarn add -D @hypequery/clibun add @hypequery/clickhouse
bun add -D @hypequery/cliConfigure your ClickHouse env vars
Create .env:
CLICKHOUSE_URL=https://example.clickhouse.cloud:8443
CLICKHOUSE_DATABASE=default
CLICKHOUSE_USER=cli_user
CLICKHOUSE_PASSWORD=super-secrethypequery generate uses these values to connect to ClickHouse during schema introspection.
Generate your schema types
Use the CLI to introspect ClickHouse and write your TypeScript schema:
npx hypequery generateThis creates the generated schema type you import into the builder. Re-run it anytime your ClickHouse schema changes.
Connect and run a typed query
import { createQueryBuilder } from '@hypequery/clickhouse';
import type { IntrospectedSchema } from './generated-schema';
const db = createQueryBuilder<IntrospectedSchema>({
url: process.env.CLICKHOUSE_URL!,
username: process.env.CLICKHOUSE_USER,
password: process.env.CLICKHOUSE_PASSWORD,
database: process.env.CLICKHOUSE_DATABASE,
});
const users = await db
.table('users')
.select(['id', 'email', 'created_at'])
.where('status', 'eq', 'active')
.orderBy('created_at', 'DESC')
.limit(10)
.execute();If that is all you need, continue with Query Basics. Or read more about connecting to ClickHouse.
Route 2: Reusable queries and serve runtime
In this route you will:
- build a typed ClickHouse query
- wrap that query with object-style
query({ ... })when it becomes reusable - run it locally with
query.execute() - expose the same query over HTTP with
serve({ queries })
This is the right route when the same query should have a stable name, validation, docs, OpenAPI, or HTTP handlers.
Install the packages
npm install @hypequery/clickhouse @hypequery/serve zod
npm install -D @hypequery/clipnpm add @hypequery/clickhouse @hypequery/serve zod
pnpm add -D @hypequery/cliyarn add @hypequery/clickhouse @hypequery/serve zod
yarn add -D @hypequery/clibun add @hypequery/clickhouse @hypequery/serve zod
bun add -D @hypequery/cliScaffold the analytics folder
Run:
npx hypequery initThe CLI will create a structure like:
Build the query, then wrap it
Open analytics/queries.ts. First build the query with the typed builder, then wrap it with query({ ... }) so it becomes a reusable definition:
import { initServe } from '@hypequery/serve';
import { z } from 'zod';
import { db } from './client';
const { query, serve } = initServe({
context: () => ({ db }),
basePath: '/api/analytics',
});
const activeUsersQuery = ({ limit }: { limit: number }) =>
db
.table('users')
.select(['id', 'email', 'created_at'])
.where('status', 'eq', 'active')
.orderBy('created_at', 'DESC')
.limit(limit)
.execute();
const activeUsers = query({
description: 'Most recent active users',
input: z.object({
limit: z.number().min(1).max(500).default(50),
}),
output: z.array(z.object({
id: z.string(),
email: z.string(),
created_at: z.string(),
})),
query: async ({ ctx, input }) => {
return activeUsersQuery({ limit: input.limit });
},
});
export const api = serve({
queries: { activeUsers },
});
api.route('/active-users', api.queries.activeUsers, { method: 'POST' });The important distinction:
activeUsersQuery(...)is plain typed query-builder logicactiveUsers = query({ ... })turns that logic into a named reusable contractserve({ queries: { activeUsers } })exposes that contract through a runtime
Run the query locally
Because activeUsers is executable, you can call it without HTTP:
const latest = await activeUsers.execute({
input: { limit: 25 },
});
console.log(latest);You can also execute through the exported API:
const latest = await api.run('activeUsers', {
input: { limit: 25 },
});Preview docs and routes
Start the dev server:
npx hypequery dev analytics/queries.tsWith basePath: '/api/analytics', the runtime exposes:
- docs at
http://localhost:4000/api/analytics/docs - OpenAPI at
http://localhost:4000/api/analytics/openapi.json - your route at
http://localhost:4000/api/analytics/active-users
Try it:
curl -X POST http://localhost:4000/api/analytics/active-users \
-H "Content-Type: application/json" \
-d '{"limit": 10}'What to learn next
- Use Query Basics if you want to stay at the raw builder layer
- Read Re-using Queries for the distinction between builder queries and
query({ ... }) - Read Core Concepts for the full builder, query, and serve model
- See Next.js, Vite, or Node.js once you are ready to mount the runtime inside a framework