> hypequery
API

React hooks

Use @hypequery/react to generate typed hooks from your hypequery API

React hooks

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.

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 { InferApiType } from '@hypequery/serve';
import { api } from '@/analytics/queries';

// 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.

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',
});

Wrap your app:

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

const queryClient = new QueryClient();

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

useQuery

const { data, error, isLoading } = useQuery('weeklyRevenue', {
  startDate: '2025-01-01',
});
  • name is one of your API keys
  • input matches the endpoint’s input
  • Options mirror TanStack Query (staleTime, enabled, etc.)

useMutation

const rebuild = useMutation('rebuildMetrics');
rebuild.mutate({ force: true });

Mutations share the same typed input/output data.

createHooks API

createHooks accepts a config object that controls base URL, HTTP method overrides, and request customization.

interface CreateHooksConfig<TApi> {
  baseUrl: string;                           // Required: base URL for your serve routes
  fetchFn?: typeof fetch;                    // Optional: custom fetch (e.g., auth/tenant headers)
  headers?: HeaderMap | (() => HeaderMap | Promise<HeaderMap>); // Optional: static headers or a (optionally async) resolver, called per request
  config?: Record<string, { method?: string; path?: string }>; // Optional: per-route HTTP method/path overrides
  manifest?: RouteManifest;                  // Optional: route manifest from `api.manifest()`; required for metric/dataset endpoints
  onUnauthorized?: () => void | Promise<void>; // Optional: called on 401; refresh creds, then the request retries once
  api?: TApi;                                // Optional: API object to infer routes automatically (reads `manifest()`)
}

Use fetchFn when you need full control. Use headers when values are static, or pass a function when you need per-request headers without rewriting fetchFn.

const { useQuery, useMutation } = createHooks<Api>({
  baseUrl: '/api',
  headers: () => {
    const tenantKey = getTenantKey();
    return tenantKey ? { 'x-tenant-key': tenantKey } : {};
  },
});

createAnalyticsHooks API

When your API registers metrics or datasets, use createAnalyticsHooks. It returns everything createHooks returns, plus four semantic hooks:

  • useMetric(name, input?, options?) — query a named metric
  • useDataset(name, input?, options?) — query a dataset (maps to the dataset:<name> route)
  • useInfiniteMetric(name, input, options?) — offset-paginated metric query
  • useInfiniteDataset(name, input, options?) — offset-paginated dataset query

CreateAnalyticsHooksConfig extends CreateHooksConfig with an optional metrics array that narrows the keys accepted by useMetric.

import { createAnalyticsHooks } from '@hypequery/react';
import { InferApiType } from '@hypequery/serve';
import { api } from '@/analytics/queries';

type Api = InferApiType<typeof api>;

export const {
  useQuery,
  useMutation,
  useMetric,
  useDataset,
  useInfiniteMetric,
  useInfiniteDataset,
} = createAnalyticsHooks<Api>({
  baseUrl: '/api',
  manifest: api.manifest(), // required to resolve metric/dataset routes
});

Semantic endpoints return { data, meta }, so read rows from result.data. The infinite hooks advance pages using meta.pagination (requested via the x-include-meta header); set limit as the page size. Without a manifest or matching config entry, the semantic hooks throw rather than request a wrong URL.

HTTP Method Configuration

By default, hooks issue GET requests (matching the default route method). Override them per query when you need POST, PUT, etc.:

import { createHooks } from '@hypequery/react';
import { InferApiType } from '@hypequery/serve';
import { api } from '@/analytics/queries';

type Api = InferApiType<typeof api>;

export const { useQuery, useMutation } = createHooks<Api>({
  baseUrl: '/api',
  config: {
    weeklyRevenue: { method: 'GET' },    // Read-only queries
    tripStats: { method: 'GET' },
    rebuildMetrics: { method: 'POST' },  // Write operations
  },
});

Advanced: Auto-config from Server

You don’t have to maintain the verbs manually—@hypequery/serve can tell the client which HTTP method each route uses.

Option A – share the server module (Next.js / Remix):

When your React code can import the server bundle (e.g., Next.js App Router), just pass api directly:

import { createHooks } from '@hypequery/react';
import { InferApiType } from '@hypequery/serve';
import { api } from '@/analytics/queries';

type Api = InferApiType<typeof api>;

export const { useQuery, useMutation } = createHooks<Api>({
  baseUrl: '/api/hypequery',
  api, // method metadata is extracted automatically
});

Option B – expose a config endpoint (SPAs / Vite):

If your frontend can’t import the server module, publish the config once and hydrate createHooks with it:

// app/api/hypequery-config/route.ts
import { extractClientConfig } from '@hypequery/serve';
import { api } from '@/analytics/queries';

export function GET() {
  return Response.json(extractClientConfig(api));
}
// lib/analytics.ts
import { createHooks } from '@hypequery/react';
import type { InferApiType } from '@hypequery/serve';

type Api = InferApiType<typeof import('@/analytics/queries').api>;

let hooksPromise: Promise<ReturnType<typeof createHooks<Api>>> | null = null;

export function getHypequeryHooks() {
  if (!hooksPromise) {
    hooksPromise = fetch('/api/hypequery-config')
      .then((res) => res.json())
      .then((config) =>
        createHooks<Api>({ baseUrl: '/api/hypequery', config })
      );
  }
  return hooksPromise;
}

Call await getHypequeryHooks() once during app bootstrap and reuse the returned hook helpers throughout your app.

Advanced options

Both hooks accept the underlying TanStack options, so you can control staleTime, retries, invalidation, manual queryClient access, etc.

Error handling

Errors bubble through TanStack Query’s error state. Validation errors from hypequery endpoints arrive as structured JSON (status code + body). Network failures throw standard fetch errors.

When to use it

  • SPA dashboards needing cached queries
  • Next.js / Remix apps already using TanStack Query
  • Agents or components calling hypequery APIs inline

If you don’t need React hooks, you can call api.execute directly on the server or use the HTTP API as-is.

On this page