Manual Installation
Install packages, configure env vars, generate schema types, and create a typed ClickHouse client.
Manual Installation
Use this page as a setup reference when you do not want to start from hypequery init.
Prerequisites
- Node.js 18+
- ClickHouse credentials
1. Install 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/cli2. Configure env vars
Create .env:
CLICKHOUSE_HOST=https://example.clickhouse.cloud:8443
CLICKHOUSE_DATABASE=default
CLICKHOUSE_USERNAME=cli_user
CLICKHOUSE_PASSWORD=super-secret3. Generate schema types
npx hypequery generate --output analytics/schema.tsRe-run this anytime your ClickHouse schema changes.
4. Create the typed client
Create analytics/client.ts:
import 'dotenv/config';
import { createQueryBuilder } from '@hypequery/clickhouse';
import type { IntrospectedSchema } from './schema';
export const db = createQueryBuilder<IntrospectedSchema>({
host: process.env.CLICKHOUSE_HOST!,
database: process.env.CLICKHOUSE_DATABASE!,
username: process.env.CLICKHOUSE_USERNAME!,
password: process.env.CLICKHOUSE_PASSWORD,
});5. Build a simple query
Create analytics/example.ts:
import { db } from './client'; const lastUsers = db .table('users') .select(['id', 'email', 'created_at']) .where('status', 'eq', 'active') .orderBy('created_at', 'DESC') .limit(10);
This is the first useful step in hypequery: a typed ClickHouse query.
6. Need to share it?
Once that builder query needs a name, typed input, or reuse across your app, wrap it with query({ ... }).
Create analytics/queries.ts:
import { initServe } from '@hypequery/serve'; import { z } from 'zod'; import { db } from './client'; const { query } = initServe({ context: () => ({ db }), }); export const latestUsers = query({ description: 'Most recent active users', input: z.object({ limit: z.number().min(1).max(100).default(10), }), query: ({ ctx, input }) => ctx.db .table('users') .select(['id', 'email', 'created_at']) .where('status', 'eq', 'active') .orderBy('created_at', 'DESC') .limit(input.limit) .execute(), });
Execute it locally:
const rows = await latestUsers.execute({ input: { limit: 25 }, }); console.log(rows);
This is the point where a local builder query becomes a reusable contract you can execute in-process.
7. Continue with the main flow
- Use Query Basics to keep learning the raw builder chain
- Use Core Concepts to understand when to stay at the query layer and when to add the runtime