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
Option 1: Automatic Type Inference (Recommended)
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> ); }