Installation Options
π Serve Framework (Recommended)
Best for: Teams that need:
- To reuse the same analytics across multiple contexts (APIs, cron jobs, internal tools, agents)
- To easily integrate authenticatation / multi-tenancy permissions to queries
- To expose queries via HTTP or React
- Auto-generated HTTP docs and OpenAPI schemas
npm install @hypequery/clickhouse @hypequery/serve
Define you queries inside the definseServe function:
// Define your analytics once
export const api = defineServe({
context: () => ({ db }),
queries: {
activeUsers: {
input: z.object({ role: z.string().optional() }),
query: ({ ctx, input }) =>
ctx.db.table('users')
.where('status', 'eq', 'active')
.where(input.role ? ['role', 'eq', input.role] : null)
.select(['id', 'name', 'email'])
.execute()
}
}
});
Then execute where you need it:
// In-process (cron jobs, background workers)
const users = await api.run('activeUsers', { role: 'admin' });
// HTTP endpoint (automatic OpenAPI)
app.use('/api', serveHTTP(api));
// React hooks (optional @hypequery/react package)
const { data } = useQuery('activeUsers', { role: 'admin' });
What you get with serve:
- β Named, reusable query definitions - No more duplicating SQL logic
- β Input validation - Zod schemas validate inputs automatically
- β Type inference - Full type safety from definition to execution
- β Runtime flexibility - Execute embedded or expose via HTTP
- β Authentication & multi-tenancy - Built-in when you need it
- β Auto-generated OpenAPI - HTTP layer with zero config
π§ Standalone Query Builder
Best for: Building ad hoc queries with type safety
npm install @hypequery/clickhouse
When to choose standalone:
- Youβre building simple scripts or one-off queries
- You want maximum control without any framework
- You donβt need to reuse the same query across multiple contexts
Example:
// Direct query execution
import { createQueryBuilder } from '@hypequery/clickhouse';
import type { Schema } from './generated-schema';
const db = createQueryBuilder<Schema>({
host: 'http://localhost:8123',
database: 'default'
user: 'default',
password: 'your-password'
});
// Execute queries directly - no query definitions
const users = await db.table('users')
.where('status', 'eq', 'active')
.select(['id', 'name', 'email'])
.execute();
Trade-offs:
- β Type-safe query builder
- β Works with any framework
- β Lightweight (no extra dependencies)
- β Queries arenβt reusable (duplication across codebase)
- β No input validation
- β No built-in HTTP/OpenAPI
- β Manual auth/multi-tenancy
Continue to Standalone Setup β
Adding React Hooks (Optional)
If youβre using serve and want type-safe React hooks:
npm install @hypequery/react
import { createHooks } from '@hypequery/react';
import { api } from './api';
const { useQuery } = createHooks(api);
function UserList() {
// Full type inference from your query definitions
const { data } = useQuery('activeUsers', { role: 'admin' });
return <div>{data?.map(u => u.name)}</div>;
}
React hooks work with serve, not standalone.
Can I switch later?
Yes! Start standalone and add serve later:
npm install @hypequery/serve
Your existing query builder code works the same way inside defineServe:
// Before: Standalone
const users = await db.table('users')
.where('status', 'eq', 'active')
.execute();
// After: Inside serve (same query builder API)
export const api = defineServe({
queries: {
activeUsers: {
query: ({ ctx }) =>
ctx.db.table('users')
.where('status', 'eq', 'active')
.execute()
}
}
});
Next Steps
Chose serve? Core Concepts - Understand the architecture
Chose standalone? When to Use Standalone - Use cases and setup
Want to explore first? Building Queries - Learn the query builder API