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.
Install and set up
npm install @hypequery/react @tanstack/react-queryPeer dependencies: react and react-dom ^18 || ^19, and @tanstack/react-query@^5.104.5. Type the hooks with
InferApiType<typeof api> and a manifest from hypequery generate:manifest (TypeScript), a client generated by
hypequery generate:client (Python or cloud), or hand-written types. The
React guide explains which to use.
useQuery
const { data, error, isLoading } = useQuery('weeklyRevenue', {
startDate: '2025-01-01',
});nameis one of your API keysinputmatches the endpoint’sinput- 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, routes, authentication and request customization. Omit
the config to read the client from a HypequeryProvider
instead.
interface CreateHooksConfig<TApi> {
baseUrl: string; // Origin or base path for routes the manifest does not cover
manifest?: RouteManifest; // Method and full path per key, from `hypequery generate:manifest` or a generated client
config?: Record<string, { method?: string; path?: string }>; // Per-route method/path overrides, applied on top of the manifest
token?: string | (() => string | undefined | Promise<string | undefined>); // Bearer token, or a per-request resolver
headers?: HeaderMap | (() => HeaderMap | Promise<HeaderMap>); // Static headers or a (optionally async) resolver, called per request
cacheKey?: string; // TanStack cache scope; a new scope per client when omitted
fetchFn?: typeof fetch; // Custom fetch implementation
onUnauthorized?: () => void | Promise<void>; // Called on 401; refresh credentials, then the request retries once
api?: TApi; // Reads routes from `api.manifest()`; server-only modules only
}Manifest paths are absolute, so when a manifest covers a route, baseUrl contributes only its origin (when it is a full
URL). Pass api only in code that never reaches the browser: it is a value import of your server module.
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/analytics',
manifest, // from `hypequery generate:manifest`
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 metricuseDataset(name, input?, options?)— query a dataset (maps to thedataset:<name>route)useInfiniteMetric(name, input, options?)— offset-paginated metric queryuseInfiniteDataset(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 type { InferApiType } from '@hypequery/serve';
import type { api } from '@/analytics/api';
// npx hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json
import manifest from '@/analytics/hypequery-manifest.json';
type Api = InferApiType<typeof api>;
export const {
useQuery,
useMutation,
useMetric,
useDataset,
useInfiniteMetric,
useInfiniteDataset,
} = createAnalyticsHooks<Api>({
baseUrl: '/api/analytics',
manifest, // resolves query, metric and dataset routes
});Semantic endpoints return { data }, plus meta when it is requested, 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 methods
Routes in the manifest use the server's method. For routes the manifest does not cover, useQuery issues GET and
useMutation issues POST; override a route with config: { rebuildMetrics: { method: 'POST' } }. See HTTP method configuration
and auto-config from server.
Advanced options
Both hooks accept the underlying TanStack options, so you can control staleTime, retries, invalidation, manual queryClient access, etc.
Exports
| Export | Purpose |
|---|---|
createHooks<Api>(config?) | useQuery, useMutation and useInfiniteQuery for an API type |
createAnalyticsHooks<Api>(config?) | Everything createHooks returns, plus useDataset, useMetric, useInfiniteDataset and useInfiniteMetric |
createHypequeryClient<Api>(config) | A request client with its own baseUrl, manifest, token, headers and cacheKey |
HypequeryProvider | Provides a client (and optionally a QueryClient) to hooks created without a config |
useHypequeryClient() | Reads the provided client, for example its cacheKey |
queryOptions(options) | Marks an object as TanStack options, for calls where it could be read as input |
HttpError | The error type for non-2xx responses, with status and parsed body |
ClientSchemaChangedError | An HttpError for a 409 CLIENT_SCHEMA_CHANGED: the endpoint changed since the client was generated |
Types: QueryInput, QueryOutput, QueryOutputForInput, HttpMethod, ApiContract, HypequeryClient,
HypequeryClientConfig, HeadersInput, TokenInput, QueryMethodConfig, RouteManifest, RouteManifestEntry,
HypequeryProviderProps, CreateHooksConfig and CreateAnalyticsHooksConfig.
Error handling
Errors surface through TanStack Query's error state. A non-2xx response rejects with HttpError: status is the HTTP
status and body the parsed response ({ error: { type, message, details? } } for hypequery endpoints; typed unknown
because proxies can return anything). message is a generic description of the failed request. Network failures reject
with the underlying fetch error. See Error Handling.
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.