# Query factories

> Define each query once as a factory of one serializable input, shared by loaders, server components and components.

A query is a **factory**: a function of zero or one argument that returns `queryOptions(...)` or `infiniteQueryOptions(...)`. The loader and the component call the same factory, so data types flow end to end.

```ts title="src/queries.ts"
import { group, infiniteQueryOptions, queryOptions } from "@raf-query/react";

export const posts = group("posts");

export function postQuery(input: { slug: string }) {
	return queryOptions({
		queryKey: posts("bySlug", input.slug),
		queryFn: () => getPost(input.slug),
		staleTime: 30_000,
	});
}

export function commentsQuery(input: { slug: string }) {
	return infiniteQueryOptions({
		queryKey: posts("comments", input.slug),
		queryFn: (context) =>
			listComments({ slug: input.slug, cursor: context.pageParam }),
		initialPageParam: 0,
		getNextPageParam: (lastPage) => lastPage.nextCursor,
	});
}
```

## One input, serializable

Factories take **at most one argument**; the type rejects two or more. That argument is the query's **input**, and it must be serializable, because it's what a [handle](https://raf-query.katt.dev/docs/handles.md) carries from the loader (or the server) to the component.

Use an object even for one field (`{ slug }`, `{ id }`): it reads well at call sites and leaves room to grow.

```ts
await load(qc, {
	post: prefetch(postQuery, { slug: "hello-world" }),
	feed: prefetch(feedQuery), // a factory without input
});
```

A factory with two parameters doesn't type-check where it's used:

```ts
function postPageQuery(slug: string, page: number) {
	return queryOptions({
		queryKey: posts(slug, page),
		queryFn: () => getPost(slug),
	});
}

await load(qc, {
	post: prefetch(postPageQuery, "hello-world"),
});
```

## The factory decides everything

The factory is the only place a query is declared, including whether it's paged. Every other API — `prefetch`, `swr`, `revalidate`, `waterfallHandle`, `invalidateOne`, `setData` — takes either kind and infers the rest from the factory's type: the data, the input, and for paged queries the page and page param types. Components read the two kinds with their own hooks, `useQuery` and `useInfiniteQuery`.

Options set in the factory override the client's (`staleTime`, `gcTime`, `structuralSharing`):

| Option              | What it does                                                                                                    |
| ------------------- | --------------------------------------------------------------------------------------------------------------- |
| `queryKey`          | The cache key; include the input. See [Query keys](https://raf-query.katt.dev/docs/query-keys.md)                                             |
| `queryFn`           | Loads the data. See [Query functions](https://raf-query.katt.dev/docs/query-functions.md)                                                     |
| `select`            | Derives what readers get from the cached data                                                                   |
| `staleTime`         | How long data counts as fresh, in ms                                                                            |
| `gcTime`            | How long an unread entry stays cached, in ms                                                                    |
| `structuralSharing` | See [Render optimizations](https://raf-query.katt.dev/docs/render-optimizations.md)                                                           |
| `stream`            | Folds the chunks of a `queryFn` that returns an async iterable. See [Streaming](https://raf-query.katt.dev/docs/query-functions.md#streaming) |
| `meta`              | Settings for the client's links and features, such as `retry`. See [Query meta](https://raf-query.katt.dev/docs/query-meta.md)                |

Per-query retries, polling and focus refetching are `meta` fields, read by the link or feature that does them: `meta: { retry: 1 }` with `retry()`, `meta: { refetchInterval: 5000 }` with `polling()`. See [Retries](https://raf-query.katt.dev/docs/retries.md), [Polling](https://raf-query.katt.dev/docs/polling.md) and [Window focus refetching](https://raf-query.katt.dev/docs/window-focus-refetching.md).

`infiniteQueryOptions` also takes `initialPageParam`, `getNextPageParam`, `getPreviousPageParam` and `maxPages`. See [Infinite queries](https://raf-query.katt.dev/docs/infinite-queries.md).

## Type inference

TypeScript infers the data type from `queryFn`. For paged queries it infers the page type from `queryFn` too, which means `queryFn` must come before `getNextPageParam` in the object literal. If your lint setup sorts keys, annotate the page instead:

```ts
const commentsOptions = infiniteQueryOptions({
	getNextPageParam: (lastPage: CommentPage) => lastPage.nextCursor,
	initialPageParam: 0,
	queryFn: (context) =>
		listComments({ cursor: context.pageParam, slug: "hello-world" }),
	queryKey: comments("hello-world"),
});
commentsOptions;
// const commentsOptions: InfiniteQueryOptions<readonly ["comments", "hello-world"], CommentPage, number, InfiniteData<CommentPage, number>, DefaultTypes>
```

To name a factory's types elsewhere:

```ts
import type { DataOf, InputOf, QueryHandle } from "@raf-query/react";

type PostData = DataOf<typeof postQuery>; // what readers get
// type PostData = Post
type PostInput = InputOf<typeof postQuery>;
// type PostInput = {
//     slug: string;
// }
type PostHandle = QueryHandle<typeof postQuery>; // for props
```

The key's shape is part of the factory's type too (`readonly ["posts", "bySlug", string]`), so handles of two factories with the same input and data are still [different types](https://raf-query.katt.dev/docs/handles.md).

## Factory makers

A library can wrap a typed API in a function that returns a factory, generic over the endpoint. Name the factory it returns, and use that wherever a factory goes; the types flow from the endpoint:

```ts
function endpointQuery<TArgs, TData>(endpoint: Endpoint<TArgs, TData>) {
	const own = group(endpoint.name);
	return (args: TArgs) =>
		queryOptions({
			queryKey: own(args),
			queryFn: (): Promise<TData> => call(endpoint, args),
		});
}

const getPostQuery = endpointQuery(api.posts.get);

const [handles] = await load(qc, {
	post: swr(getPostQuery, { slug }),
});
const [postThenable] = useQuery(getPostQuery, handles.post);
const post = use(postThenable);
```

Each call of `endpointQuery` returns a new function, but entries are found by `queryKey`, so every call for the same endpoint and args reaches the same entry. Put everything the key depends on in it. [Invalidating](https://raf-query.katt.dev/docs/query-invalidation.md#targets) by factory matches by identity, though, so name the factory once, as here, or invalidate a group in its key.

## Server-only factories

A factory whose `queryFn` imports server-only code (a database client) can't be imported by a client component, so it can't be read with `useQuery` in the browser. Render its data in a server component, or call the database through a server function or an API route so the browser can refetch it. See [React Server Components: server and browser requests](https://raf-query.katt.dev/docs/react-server-components.md#server-and-browser-requests) and [TanStack Start: server and browser requests](https://raf-query.katt.dev/docs/tanstack-start.md#server-and-browser-requests).
