End-to-end type safety
Path params, search params, request bodies, response shapes, and error
payloads are all inferred from a single RouteDefinition map. No any
leaks into your components.
use-q is a thin, opinionated layer that turns an OpenAPI-style schema into fully typed, cache-aware React hooks — without giving up the power of TanStack Query.
End-to-end type safety
Path params, search params, request bodies, response shapes, and error
payloads are all inferred from a single RouteDefinition map. No any
leaks into your components.
Framework-agnostic core
@use-q/api-client is a tiny zero-dependency fetcher that runs in Node,
Bun, Deno, edge workers, or the browser. Use it from server actions,
loaders, or scripts — no React required.
Schema-driven invalidation
Tag routes with tags/invalidatesTags and the React layer takes care of
the rest. Mutations automatically refresh the right queries through a
shared TagRegistry.
Optimistic updates that compose
Update multiple caches per mutation with snapshot/rollback semantics built
in. Combine with additionalInvalidatesTags for arbitrary fan-out.
OpenAPI codegen
Generate a typed schema directly from an OpenAPI 3.x spec with
use-q-codegen. Pagination is detected automatically.
Bring your own QueryClient
Reuse a single QueryClient across multiple createApiClient instances,
or hook into persistence and devtools — use-q never gets in the way.
import { createApiClient } from "@use-q/api-client-react";
const schema = { listPosts: { method: "GET", path: "/facilities/{facilityId}/posts", pathParams: {} as { facilityId: string }, response: {} as { id: string; title: string }[], tags: [{ type: "post", facilityId: (p) => p.facilityId }], }, createPost: { method: "POST", path: "/facilities/{facilityId}/posts", pathParams: {} as { facilityId: string }, body: {} as { title: string }, response: {} as { id: string; title: string }, invalidatesTags: [{ type: "post", facilityId: (p) => p.facilityId }], },} as const;
export const api = createApiClient<typeof schema>(schema, { baseUrl: "https://api.example.com",});
export const { useQ, useM, useQClient } = api;Then in a component:
function PostList({ facilityId }: { facilityId: string }) { const { data, isLoading } = api.useQ("listPosts", { pathParams: { facilityId } }); const createPost = api.useM("createPost", { pathParams: { facilityId } });
if (isLoading) return <p>Loading…</p>; return ( <> {data?.map((p) => <article key={p.id}>{p.title}</article>)} <button onClick={() => createPost.mutate({ body: { title: "New post" } })}> Add </button> </> );}That’s it — list refetches automatically after the mutation thanks to schema-defined tags.
Install
5-minute quick start
Define a schema
API reference