> hypequery

Getting Started

Install and set up @hypequery/react for type-safe hooks

React Hooks - Getting Started

Use @hypequery/react to generate type-safe hooks (useQuery, useMutation) backed by TanStack Query. Bring your exported serve({ queries }) API type into your React app without duplicating schemas.

For semantic metrics and datasets endpoints, use createAnalyticsHooks, which adds useMetric and useDataset on top of the base hooks. See Analytics hooks for metrics and datasets below.

Installation

npm install @hypequery/react @tanstack/react-query

Peer dependencies: react@^18, @tanstack/react-query@^5.

Setup

Use InferApiType to automatically extract types from your API definition:

// lib/analytics.ts
import { createHooks } from '@hypequery/react';
import type { InferApiType } from '@hypequery/serve';
import type { api } from '@/analytics/api';

// Automatic type inference - no manual type definition needed!
type Api = InferApiType<typeof api>;

export const { useQuery, useMutation } = createHooks<Api>({
  baseUrl: '/api', // where your hypequery routes live
});

This eliminates the need to manually define and maintain a separate type for your API.

Both @hypequery/serve imports here are import type, so they are erased at build time and no server code reaches the client bundle. Keep it that way — this module is imported by client components.

Option 2: Manual Type Definition

If you prefer to manually define your API types:

// lib/analytics.ts
import { createHooks } from '@hypequery/react';

type Api = {
  weeklyRevenue: {
    input: { startDate: string };
    output: { total: number };
  };
  // ... other queries
};

export const { useQuery, useMutation } = createHooks<Api>({
  baseUrl: '/api',
});

Analytics hooks for metrics and datasets

When your API registers metrics or datasets, use createAnalyticsHooks instead of createHooks. It returns everything createHooks does, plus useMetric, useDataset, useInfiniteMetric, and useInfiniteDataset.

Semantic endpoints are POST routes whose paths differ from their map keys (e.g. the orders dataset lives at POST /datasets/orders/query), so the hooks need a manifest to resolve them.

Generate the manifest as static JSON and import the type of your API, never its value:

npx hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json
// lib/analytics.ts
import { createAnalyticsHooks } from '@hypequery/react';
import type { InferApiType } from '@hypequery/serve';
import type { api } from '@/analytics/api';
import manifest from '@/analytics/hypequery-manifest.json';

// `import type` is erased at build time, so the server module — and the
// ClickHouse client it constructs — never reaches the browser bundle.
type Api = InferApiType<typeof api>;

export const {
  useQuery,
  useMutation,
  useMetric,
  useDataset,
  useInfiniteMetric,
  useInfiniteDataset,
} = createAnalyticsHooks<Api>({
  baseUrl: '/api',
  manifest,
});

Re-run generate:manifest whenever you add or re-route an endpoint. Wire it into your build ("prebuild": "hypequery generate:manifest …") so it cannot drift.

Do not import the serve API as a value here

This module is imported by client components, so anything it imports as a value is bundled for the browser. Calling manifest: api.manifest() requires a value import of api, which pulls initServe and @hypequery/clickhouse into the client graph. In a Next.js App Router project that fails the build outright:

./node_modules/@hypequery/clickhouse/dist/cli/generate-types.js:2:1
Error: Module not found: Can't resolve 'fs/promises'

api.manifest() is a server-side API. Use it in your build step, in a config endpoint, or anywhere else that only runs on the server — not in the module that exports your hooks.

api.manifest() output is plain JSON, so you can also serve it from a small endpoint and fetch it at runtime when a static file does not suit your setup — see Auto-config from server. Without a manifest (or an explicit config entry), useMetric/useDataset throw a clear error rather than calling the wrong URL.

Provider Setup

Wrap your app with TanStack Query's provider:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}
// app.tsx or _app.tsx
import { AppProviders } from './providers';

function App() {
  return (
    <AppProviders>
      <YourApp />
    </AppProviders>
  );
}

On this page