Skip to content

raf-query is early. The API can still change between minor releases, so use it at your own risk.

raf-query

Guides

Query cancellation

Pass the queryFn's AbortSignal to fetch so requests nobody needs any more are aborted, and know when raf-query aborts them.

Every queryFn receives an AbortSignal as signal. Pass it to fetch (or any API that takes a signal), and raf-query can abort the request when its result is no longer needed:

export const const search: Group<readonly ["search"]>search = group<"search">(name: "search"): Group<readonly ["search"]> (+1 overload)

A group, or a subgroup inside parent. Call it for a query's key: a flat, plain array that starts with the group's path. Invalidating a group covers its subgroups and every query below them.

group
("search");
export function
function searchQuery(input: {
    q: string;
}): QueryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[], DefaultTypes>
searchQuery
(
input: {
    q: string;
}
input
: { q: stringq: string }) {
return
queryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[]>(options: BaseOptions<readonly ["search", string], DefaultTypes> & {
    readonly queryFn: (context: QueryFunctionContext<readonly ["search", string]>) => Awaitable<readonly SearchResult[]>;
    readonly select?: ((data: readonly SearchResult[]) => readonly SearchResult[]) | undefined;
}): QueryOptions<readonly ["search", string], readonly SearchResult[], readonly SearchResult[], DefaultTypes> (+1 overload)

Typed options for a query factory:

queryOptions
({
BaseOptions<readonly ["search", string], DefaultTypes>.queryKey: readonly ["search", string]

A plain array, usually built by a group(). Handles keep its literal type.

queryKey
:
const search: Group
<readonly [string]>(rest_0: string) => readonly ["search", string]
search
(
input: {
    q: string;
}
input
.q: stringq),
queryFn: (context: QueryFunctionContext<readonly ["search", string]>) => Awaitable<readonly SearchResult[]>queryFn: async (context: QueryFunctionContext<readonly ["search", string]>context) => { const const response: Responseresponse = await function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)fetch( `/api/search?q=${function encodeURIComponent(uriComponent: string | number | boolean): string

Encodes a text string as a valid component of a Uniform Resource Identifier (URI).

@paramuriComponent — A value representing an unencoded URI component.
encodeURIComponent
(
input: {
    q: string;
}
input
.q: stringq)}`,
{ RequestInit.signal?: AbortSignal | null | undefined

An AbortSignal to set request's signal.

signal
: context: QueryFunctionContext<readonly ["search", string]>context.QueryFunctionContext<readonly ["search", string]>.signal: AbortSignal

Aborted when the request is restarted or its entry is removed.

signal
},
); return (await const response: Responseresponse.Body.json(): Promise<any>json()) as interface ReadonlyArray<T>ReadonlyArray<SearchResult>; }, }); }

A queryFn that ignores the signal still works. Its request just runs to the end, and if raf-query no longer wants it, the result is dropped.

When requests are aborted

raf-query aborts a request only when it replaces or drops it:

  • A query on screen is invalidated, or any query is reset, while a request is in flight. That request may have read data from before the write, so it's aborted and a new one starts. Whoever was waiting for the old request gets the new one's result.
  • The query is removed (removeOne, removeMany, removeAll). Its request is aborted, and a load waiting for it rejects with an AbortError, as an aborted fetch would.
  • A timeout(ms) link gives up on it. See Retries and errors.

Nothing else aborts a request. When the last component reading a query unmounts, its request runs to the end and the result is cached: the query is likely to be read again, and gcTime only removes an entry once its request has finished. Requests nothing has read yet, like a loader's prefetch, run to the end too.

Try it below. The inbox's queryFn passes its signal to the server, so aborted requests show up in the Queries panel:

Cancelling yourself

To stop a request yourself, remove the query with removeOne(queryClient, factory, input): the request is aborted and the data dropped. There's no call that aborts a request and keeps the query; in practice, letting a request finish and caching its result is almost always what you want.

Using axios or other clients

Clients that accept a signal work the same way, such as axios:

queryOptions<readonly ["posts", "bySlug", string], unknown, unknown>(options: BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes> & {
    readonly queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknown;
    readonly select?: ((data: unknown) => unknown) | undefined;
}): QueryOptions<readonly ["posts", "bySlug", string], unknown, unknown, DefaultTypes> (+1 overload)

Typed options for a query factory:

queryOptions
({
BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes>.queryKey: readonly ["posts", "bySlug", string]

A plain array, usually built by a group(). Handles keep its literal type.

queryKey
: posts<readonly ["bySlug", string]>(rest_0: "bySlug", rest_1: string): readonly ["posts", "bySlug", string]posts("bySlug",
const input: {
    slug: string;
}
input
.slug: stringslug),
queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknownqueryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context) =>
const axios: {
    get(url: string, options: {
        signal: AbortSignal;
    }): Promise<unknown>;
}
axios
.
function get(url: string, options: {
    signal: AbortSignal;
}): Promise<unknown>
get
(`/api/posts/${
const input: {
    slug: string;
}
input
.slug: stringslug}`, { signal: AbortSignalsignal: context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context.QueryFunctionContext<readonly ["posts", "bySlug", string]>.signal: AbortSignal

Aborted when the request is restarted or its entry is removed.

signal
}),
});

For clients that take a cancel function instead, call it from an abort listener:

queryOptions<readonly ["posts", "bySlug", string], unknown, unknown>(options: BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes> & {
    readonly queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknown;
    readonly select?: ((data: unknown) => unknown) | undefined;
}): QueryOptions<readonly ["posts", "bySlug", string], unknown, unknown, DefaultTypes> (+1 overload)

Typed options for a query factory:

queryOptions
({
BaseOptions<readonly ["posts", "bySlug", string], DefaultTypes>.queryKey: readonly ["posts", "bySlug", string]

A plain array, usually built by a group(). Handles keep its literal type.

queryKey
: posts<readonly ["bySlug", string]>(rest_0: "bySlug", rest_1: string): readonly ["posts", "bySlug", string]posts("bySlug",
const input: {
    slug: string;
}
input
.slug: stringslug),
queryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>) => unknownqueryFn: (context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context) => { const
const request: {
    cancel(): void;
    promise: Promise<unknown>;
}
request
=
const client: {
    get(url: string): {
        cancel(): void;
        promise: Promise<unknown>;
    };
}
client
.
function get(url: string): {
    cancel(): void;
    promise: Promise<unknown>;
}
get
(`/api/posts/${
const input: {
    slug: string;
}
input
.slug: stringslug}`);
context: QueryFunctionContext<readonly ["posts", "bySlug", string]>context.QueryFunctionContext<TKey extends QueryKey = QueryKey>.signal: AbortSignal

Aborted when the request is restarted or its entry is removed.

signal
.AbortSignal.addEventListener<"abort">(type: "abort", listener: (this: AbortSignal, ev: Event) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

addEventListener
("abort", () =>
const request: {
    cancel(): void;
    promise: Promise<unknown>;
}
request
.function cancel(): voidcancel());
return
const request: {
    cancel(): void;
    promise: Promise<unknown>;
}
request
.promise: Promise<unknown>promise;
}, });