Installation Options

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

Continue to Quickstart β†’


πŸ”§ 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