> hypequery

Quick Start

Get started with hypequery.

What you'll build

In this guide you will:

  • build a typed ClickHouse query with db.table(...)
  • 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 main recommended path for new projects.

Install the packages

npm install @hypequery/clickhouse @hypequery/serve zod
npm install -D @hypequery/cli
pnpm add @hypequery/clickhouse @hypequery/serve zod
pnpm add -D @hypequery/cli
yarn add @hypequery/clickhouse @hypequery/serve zod
yarn add -D @hypequery/cli
bun add @hypequery/clickhouse @hypequery/serve zod
bun add -D @hypequery/cli

Scaffold the analytics folder

Run:

npx hypequery init

The CLI will create a structure like:

schema.ts
client.ts
queries.ts
.env

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 logic
  • activeUsers = query({ ... }) turns that logic into a named reusable contract
  • serve({ 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.ts

With 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

On this page