Guides
Query meta
Attach information to queries with meta, read it in the queryFn, error handlers and devtools, and type it for the whole app.
meta attaches anything you want to know about a query elsewhere. It reaches:
- the
queryFn, ascontext.meta Query.metaUnsafe(query), from the latest factory call for the entry, wherever you get aQuery(onError, filters,getAll())- the devtools, in the entry's details
A common use is a per-query message for one global error handler:
import { function infiniteQueryOptions<TPage, TPageParam, TError = unknown, TServices = never, const TKey extends QueryKey = QueryKey, TThrow extends boolean | undefined = undefined>(options: Omit<InfiniteQueryOptions<{
error: TError;
key: TKey;
page: TPage;
pageParam: TPageParam;
services: TServices;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<TThrow>): InfiniteQueryOptions<Flat<{
key: TKey;
page: TPage;
pageParam: TPageParam;
} & ErrorPart<TError> & ServicesPart<TServices> & ThrowPart<...>>>
infiniteQueryOptions, import QueryQuery, class QueryClient<TServices = never>QueryClient } from "@raf-query/react";
export function function issuesQuery(): InfiniteQueryOptions<Flat<{
key: readonly ["issues"];
page: {
items: unknown;
nextCursor: null;
};
pageParam: number;
}>>
issuesQuery() {
return infiniteQueryOptions<{
items: unknown;
nextCursor: null;
}, number, unknown, never, readonly ["issues"], undefined>(options: Omit<InfiniteQueryOptions<{
error: unknown;
key: readonly ["issues"];
page: {
items: unknown;
nextCursor: null;
};
pageParam: number;
services: never;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<undefined>): InfiniteQueryOptions<Flat<{
key: readonly ["issues"];
page: {
items: unknown;
nextCursor: null;
};
pageParam: number;
}>>
infiniteQueryOptions({
queryKey: readonly ["issues"]queryKey: ["issues"],
function queryFn(context: PageFunctionContext<number>): QueryFnResult<{
data: {
items: unknown;
nextCursor: null;
};
error: unknown;
services: never;
}>
queryFn: async () => ({
items: unknownitems: await function listIssues(): Promise<unknown>listIssues(),
nextCursor: nullnextCursor: null,
}),
initialPageParam: numberinitialPageParam: 0,
function getNextPageParam(lastPage: {
items: unknown;
nextCursor: null;
}, pages: readonly {
items: unknown;
nextCursor: null;
}[], lastPageParam: number, pageParams: readonly number[]): number | null | undefined
getNextPageParam: (lastPage: {
items: unknown;
nextCursor: null;
}
lastPage) => lastPage: {
items: unknown;
nextCursor: null;
}
lastPage.nextCursor: nullnextCursor,
meta?: {
errorMessage?: string;
scope?: string;
} | undefined
meta: { errorMessage?: string | undefinederrorMessage: "Couldn't load issues" },
});
}
const const queryClient: QueryClient<never>queryClient = new new QueryClient<never>(config?: QueryClientConfig<never>): QueryClient<never>QueryClient({
QueryClientConfig<never>.onError?(error: unknown, query: Query.Query): voidonError: (error: unknownerror, query: Query.Query<QueryFactory>query) => {
const toast: {
error(message: string): void;
}
toast.function error(message: string): voiderror(
import QueryQuery.function metaUnsafe<QueryFactory>(self: Query.Query<QueryFactory>): QueryMeta | undefinedmetaUnsafe(query: Query.Query<QueryFactory>query)?.errorMessage?: string | undefinederrorMessage ?? "Something went wrong",
);
},
});Typing meta
meta is Record<string, unknown> by default. Type it for the whole app by augmenting Register in the package you import from (@raf-query/react re-exports it from @raf-query/core, so either works):
declare module "@raf-query/react" {
interface Register {
Register.queryMeta: {
errorMessage?: string;
}
queryMeta: { errorMessage?: string | undefinederrorMessage?: string };
}
}Every meta is then checked against that type, and Query.metaUnsafe returns it.
Defaults
config.defaults takes meta too, for queries that set none. A query's own meta replaces the default as a whole; the two aren't merged.
const const queryClient: QueryClient<never>queryClient = new new QueryClient<never>(config?: QueryClientConfig<never>): QueryClient<never>QueryClient({
Config.defaults?: QueryBehavior | undefineddefaults: { QueryBehavior.meta?: Record<string, unknown> | undefinedmeta: { errorMessage: stringerrorMessage: "Something went wrong" } },
});Selecting queries by meta
Filters receive the Query, so meta can select queries to invalidate:
await const qc: QueryClient<never>qc.QueryClient<never>.invalidate<QueryFactory<never>>(...target: Target<QueryFactory<never>>): Promise<void>invalidate({
Filter.predicate?: ((query: Query.Query) => boolean) | undefinedpredicate: (query: Query.Query<QueryFactory>query) => import QueryQuery.function metaUnsafe<QueryFactory>(self: Query.Query<QueryFactory>): QueryMeta | undefinedmetaUnsafe(query: Query.Query<QueryFactory>query)?.scope?: string | undefinedscope === "billing",
});Serialization
meta is sent with dehydrated state during SSR, so keep it serializable. A hydrated entry reports the server's meta until the browser calls its factory.