Skip to content

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

raf-query

Integrations

Using Effect

The cache is written in Effect. Return Effects from queryFns for typed errors, services and interruption, run loaders and actions as Effects, and read the same queries from React.

raf-query's cache is written in Effect. Using raf-query doesn't take any Effect: queryFns return promises, and QueryClient, the hooks and the integrations speak promises. Apps that do use Effect get the same cache as Effects, with typed errors and services.

effect (^4.0.0) is a peer dependency of both packages, so an app that uses Effect shares one copy with raf-query.

The whole picture

An issue page on TanStack Start, from the API to the form that renames the issue. The Effect example app is the same thing, in full, with Effect RPC.

The API: errors and a service

Failures are tagged errors, and the API is a service. Its methods say how they can fail:

src/issues.ts
export class class IssueNotFoundIssueNotFound extends import SchemaSchema.
const TaggedError: <IssueNotFound, {}>(identifier?: string) => {
    <Tag, Fields>(tag: Tag, fields: Fields, annotations?: Schema.Annotations.Declaration<IssueNotFound, readonly [Schema.TaggedStruct<Tag, Fields>]> | undefined): Schema.Class<IssueNotFound, Schema.TaggedStruct<Tag, Fields>, YieldableError>;
    <Tag, S>(tag: Tag, schema: S, annotations?: Schema.Annotations.Declaration<IssueNotFound, readonly [Schema.Struct<{ [K in keyof ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])]: ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])[K]; }>]> | undefined): Schema.Class<...>;
}
TaggedError
<class IssueNotFoundIssueNotFound>()(
"IssueNotFound", { id: Schema.Numberid: import SchemaSchema.const Number: Schema.NumberNumber }, ) {} export class class UnauthorizedUnauthorized extends import SchemaSchema.
const TaggedError: <Unauthorized, {}>(identifier?: string) => {
    <Tag, Fields>(tag: Tag, fields: Fields, annotations?: Schema.Annotations.Declaration<Unauthorized, readonly [Schema.TaggedStruct<Tag, Fields>]> | undefined): Schema.Class<Unauthorized, Schema.TaggedStruct<Tag, Fields>, YieldableError>;
    <Tag, S>(tag: Tag, schema: S, annotations?: Schema.Annotations.Declaration<Unauthorized, readonly [Schema.Struct<{ [K in keyof ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])]: ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])[K]; }>]> | undefined): Schema.Class<...>;
}
TaggedError
<class UnauthorizedUnauthorized>()(
"Unauthorized", {}, ) {} export class class EmptyTitleEmptyTitle extends import SchemaSchema.
const TaggedError: <EmptyTitle, {}>(identifier?: string) => {
    <Tag, Fields>(tag: Tag, fields: Fields, annotations?: Schema.Annotations.Declaration<EmptyTitle, readonly [Schema.TaggedStruct<Tag, Fields>]> | undefined): Schema.Class<EmptyTitle, Schema.TaggedStruct<Tag, Fields>, YieldableError>;
    <Tag, S>(tag: Tag, schema: S, annotations?: Schema.Annotations.Declaration<EmptyTitle, readonly [Schema.Struct<{ [K in keyof ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])]: ({
        readonly _tag: Schema.tag<Tag>;
    } & S["fields"])[K]; }>]> | undefined): Schema.Class<...>;
}
TaggedError
<class EmptyTitleEmptyTitle>()(
"EmptyTitle", {}, ) {} export class class IssueApiIssueApi extends import ContextContext.
const Service: <IssueApi, {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}>() => <Identifier, E, R, Args>(id: Identifier, options?: {
    readonly make?: ((...args: Args) => Effect.Effect<{
        readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
        readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
        readonly rename: (input: {
            readonly id: number;
            readonly title: string;
        }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
    }, E, R>) | Effect.Effect<...> | undefined;
} | undefined) => Context.ServiceClass<...> & ([...] extends [...] ? unknown : {
    ...;
}) (+2 overloads)
Service
<
class IssueApiIssueApi, { readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>get: ( id: numberid: number, ) => import EffectEffect.interface Effect<out A, out E = never, out R = never>Effect<Issue, class IssueNotFoundIssueNotFound | class UnauthorizedUnauthorized>; readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>assignee: ( issueId: numberissueId: number, ) => import EffectEffect.interface Effect<out A, out E = never, out R = never>Effect<User, class IssueNotFoundIssueNotFound | class UnauthorizedUnauthorized>; readonly
rename: (input: {
    readonly id: number;
    readonly title: string;
}) => Effect.Effect<Issue, EmptyTitle | Unauthorized>
rename
: (
input: {
    readonly id: number;
    readonly title: string;
}
input
: {
readonly id: numberid: number; readonly title: stringtitle: string; }) => import EffectEffect.interface Effect<out A, out E = never, out R = never>Effect<Issue, class EmptyTitleEmptyTitle | class UnauthorizedUnauthorized>; } >()("app/IssueApi") {}

IssueApiLive, a Layer<IssueApi>, implements it: an HTTP client, an RPC client, a database.

Queries

A queryFn returns an Effect that uses the service. The factory's type carries the service and the errors — ErrorOf and ServicesOf name them, and every cache verb fails or requires them:

src/queries.ts
import { type type ErrorOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TTypes extends InfiniteQueryTypes> ? ErrorIn<TTypes> : ReturnType<TFactory> extends QueryOptions<infer TTypes extends QueryTypes> ? ErrorIn<TTypes> : unknownErrorOf, 
function queryOptions<TData, TError = unknown, TServices = never, const TKey extends QueryKey = QueryKey, TThrow extends boolean | undefined = undefined>(options: Omit<QueryOptions<{
    data: TData;
    error: TError;
    key: TKey;
    services: TServices;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<TThrow>): QueryOptions<Flat<{
    data: TData;
    key: TKey;
} & ErrorPart<TError> & ServicesPart<TServices> & ThrowPart<TThrow>>>
queryOptions
, type type ServicesOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TTypes extends InfiniteQueryTypes> ? ServicesIn<TTypes> : ReturnType<TFactory> extends QueryOptions<infer TTypes extends QueryTypes> ? ServicesIn<TTypes> : neverServicesOf } from "@raf-query/react";
import { class IssueApiIssueApi } from "./issues.ts"; export function
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
(
input: {
    id: number;
}
input
: { id: numberid: number }) {
return
queryOptions<Issue, IssueNotFound | Unauthorized, IssueApi, readonly ["issue", number], undefined>(options: Omit<QueryOptions<{
    data: Issue;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number];
    services: IssueApi;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<undefined>): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
queryOptions
({
queryKey: readonly ["issue", number]queryKey: ["issue",
input: {
    id: number;
}
input
.id: numberid],
function queryFn(context: QueryFunctionContext): QueryFnResult<{
    data: Issue;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number];
    services: IssueApi;
}>
queryFn
: () => class IssueApiIssueApi.
Service<IssueApi, { readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>; readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>; readonly rename: (input: { readonly id: number; readonly title: string; }) => Effect<Issue, EmptyTitle | Unauthorized>; }>.use<Issue, IssueNotFound | Unauthorized, never>(f: (service: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}) => Effect<...>): Effect<...>
use
((
api: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}
api
) =>
api: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}
api
.get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>get(
input: {
    id: number;
}
input
.id: numberid)),
}); } export function
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
(
input: {
    issueId: number;
}
input
: { issueId: numberissueId: number }) {
return
queryOptions<User, IssueNotFound | Unauthorized, IssueApi, readonly ["issue", number, "assignee"], undefined>(options: Omit<QueryOptions<{
    data: User;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number, "assignee"];
    services: IssueApi;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<undefined>): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    ...;
}>>
queryOptions
({
queryKey: readonly ["issue", number, "assignee"]queryKey: ["issue",
input: {
    issueId: number;
}
input
.issueId: numberissueId, "assignee"],
function queryFn(context: QueryFunctionContext): QueryFnResult<{
    data: User;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number, "assignee"];
    services: IssueApi;
}>
queryFn
: () => class IssueApiIssueApi.
Service<IssueApi, { readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>; readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>; readonly rename: (input: { readonly id: number; readonly title: string; }) => Effect<Issue, EmptyTitle | Unauthorized>; }>.use<User, IssueNotFound | Unauthorized, never>(f: (service: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}) => Effect<...>): Effect<...>
use
((
api: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}
api
) =>
api: {
    readonly get: (id: number) => Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect<Issue, EmptyTitle | Unauthorized>;
}
api
.assignee: (issueId: number) => Effect<User, IssueNotFound | Unauthorized>assignee(
input: {
    issueId: number;
}
input
.issueId: numberissueId)),
}); } export type IssueError = type ErrorOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TTypes extends InfiniteQueryTypes> ? ErrorIn<TTypes> : ReturnType<TFactory> extends QueryOptions<infer TTypes extends QueryTypes> ? ErrorIn<TTypes> : unknownErrorOf<typeof
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
>;
type IssueError = IssueNotFound | Unauthorized
export type AssigneeError = type ErrorOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TTypes extends InfiniteQueryTypes> ? ErrorIn<TTypes> : ReturnType<TFactory> extends QueryOptions<infer TTypes extends QueryTypes> ? ErrorIn<TTypes> : unknownErrorOf<typeof
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
>;
type AssigneeError = IssueNotFound | Unauthorized
export type Services = type ServicesOf<TFactory extends QueryFactory> = ReturnType<TFactory> extends InfiniteQueryOptions<infer TTypes extends InfiniteQueryTypes> ? ServicesIn<TTypes> : ReturnType<TFactory> extends QueryOptions<infer TTypes extends QueryTypes> ? ServicesIn<TTypes> : neverServicesOf<typeof
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
>;
type Services = IssueApi

The client

The QueryClient's layer provides the services, and the router context carries the client with their type, so loaders can only start queries the layer can run:

src/router.tsx
import { class QueryClient<TServices = never>QueryClient, 
function QueryClientProvider(props: {
    readonly children?: React.ReactNode;
    readonly client: QueryClient<unknown>;
}): React.JSX.Element
QueryClientProvider
} from "@raf-query/react";
import { function setupRouterIntegration<TRouter extends AnyRouter>(options: RouterIntegrationOptions<TRouter>): voidsetupRouterIntegration } from "@raf-query/react/tanstack-router"; import { const createRouter: CreateRouterFncreateRouter } from "@tanstack/react-router"; import { const IssueApiLive: Layer<IssueApi, never, never>IssueApiLive } from "./issues.ts"; import {
const routeTree: Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>
routeTree
} from "./routeTree.gen.ts";
export function
function getRouter(): RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
getRouter
() {
const const queryClient: QueryClient<IssueApi>queryClient = new new QueryClient<IssueApi>(config?: QueryClientConfig<IssueApi>): QueryClient<IssueApi>QueryClient({ Config.defaults?: QueryBehavior | undefineddefaults: { QueryBehavior.staleTime?: number | undefinedstaleTime: 5000 }, QueryClientConfig<IssueApi>.layer?: Layer<IssueApi, never, never> | undefinedlayer: const IssueApiLive: Layer<IssueApi, never, never>IssueApiLive, }); const
const router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
=
createRouter<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>(options: RouterConstructorOptions<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, ... 7 more ..., undefined>, "never", false, RouterHistory, Record<...>>): RouterCore<...>
createRouter
({
context: {
    queryClient: QueryClient<IssueApi>;
}
context
: { queryClient: QueryClient<IssueApi>queryClient },
// The query cache decides what's fresh; loaders always run and dedupe there. defaultPreloadStaleTime?: number | undefineddefaultPreloadStaleTime: 0,
routeTree?: Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined> | undefined
routeTree
,
Wrap?: ((props: {
    children: any;
}) => React.JSX.Element) | undefined
Wrap
: (
props: {
    children: any;
}
props
) => (
<
function QueryClientProvider(props: {
    readonly children?: React.ReactNode;
    readonly client: QueryClient<unknown>;
}): React.JSX.Element
QueryClientProvider
client: QueryClient<unknown>client={const queryClient: QueryClient<IssueApi>queryClient}>
{
props: {
    children: any;
}
props
.children: anychildren}
</
function QueryClientProvider(props: {
    readonly children?: React.ReactNode;
    readonly client: QueryClient<unknown>;
}): React.JSX.Element
QueryClientProvider
>
), });
setupRouterIntegration<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>(options: RouterIntegrationOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, ... 8 more ..., undefined>, "never", false, RouterHistory, Record<...>>>): void
setupRouterIntegration
({ RouterIntegrationOptions<TRouter extends AnyRouter>.queryClient: QueryClient<never>queryClient,
RouterIntegrationOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient<IssueApi>; }, AnyContext, ... 7 more ..., undefined>, "never", false, RouterHistory, Record<...>>>.router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
});
return
const router: RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>
router
;
}
src/routes/__root.tsx
import type { class QueryClient<TServices = never>QueryClient } from "@raf-query/react";
import { function createRootRouteWithContext<TRouterContext extends {}>(): <TRegister = Register, TRouteContextFn = AnyContext, TBeforeLoadFn = AnyContext, TSearchValidator = undefined, TLoaderDeps extends Record<string, any> = {}, TLoaderFn = undefined, TSSR = unknown, TServerMiddlewares = unknown>(options?: RootRouteOptions<TRegister, TSearchValidator, TRouterContext, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TSSR, TServerMiddlewares>) => RootRoute<TRegister, TSearchValidator, TRouterContext, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, unknown, unknown, TSSR, TServerMiddlewares, undefined>createRootRouteWithContext } from "@tanstack/react-router";

import type { class IssueApiIssueApi } from "~/issues.ts";

export const 
const Route: RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>
Route
=
createRootRouteWithContext<{
    queryClient: QueryClient<IssueApi>;
}>(): <TRegister, TRouteContextFn, TBeforeLoadFn, TSearchValidator, TLoaderDeps, TLoaderFn, TSSR, TServerMiddlewares>(options?: RootRouteOptions<TRegister, TSearchValidator, {
    queryClient: QueryClient<IssueApi>;
}, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TSSR, TServerMiddlewares, undefined> | undefined) => RootRoute<...>
createRootRouteWithContext
<{
queryClient: QueryClient<IssueApi>queryClient: class QueryClient<TServices = never>QueryClient<class IssueApiIssueApi>; }>()({});

The route

Loaders use the same Promise verbs as a non-Effect app. Prefetch is sync; ensure returns a Promise. A loader that needs a typed catch is an Effect.fn, like a form action: it yield*s the QueryCache service, starts its reads together with Effect.all, and catches what it expects. runPromise runs it with the layer's services — the same shape as Effect's ManagedRuntime:

src/routes/issues.$issueId.tsx
import { import QueryCacheQueryCache, type interface QueryHandle<TFactory extends QueryFactory = (input: unknown) => FactoryResult>QueryHandle, 
function useQuery<TFactory extends QueryFactory, const TThrow extends boolean | undefined = undefined>(factory: TFactory, handle: QueryHandleWithData<TFactory>, options?: UseQueryOptions & UseQueryThrowOptions<TFactory> & {
    readonly throwOnError?: TThrow;
}): UseQueryResultWithData<TFactory, TThrow> (+1 overload)
useQuery
} from "@raf-query/react";
import { function createFileRoute<TFilePath extends keyof FileRoutesByPath, TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]["parentRoute"], TId extends RouteConstraints["TId"] = FileRoutesByPath[TFilePath]["id"], TPath extends RouteConstraints["TPath"] = FileRoutesByPath[TFilePath]["path"], TFullPath extends RouteConstraints["TFullPath"] = FileRoutesByPath[TFilePath]["fullPath"]>(path?: TFilePath): FileRoute<TFilePath, TParentRoute, TId, TPath, TFullPath>["createRoute"]createFileRoute, function notFound(options?: NotFoundError): NotFoundErrornotFound } from "@tanstack/react-router"; import { import EffectEffect, import ResultResult } from "effect"; import { const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense, function use<T>(usable: React.Usable<T>): Tuse } from "react"; import {
function RenameForm(props: {
    issue: Issue;
}): React.JSX.Element
RenameForm
} from "~/components/rename-form.tsx";
import {
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
,
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
} from "~/queries.ts";
const
const loadIssue: (id: number) => Effect.Effect<{
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}, NotFoundError | ... 1 more ... | CancelledError, IssueApi | QueryCache.QueryCache>
loadIssue
= import EffectEffect.const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)fn("loadIssue")(
function* (id: numberid: number) { const const cache: QueryCache.QueryCachecache = yield* import QueryCacheQueryCache.const QueryCache: Service<QueryCache.QueryCache, QueryCache.QueryCache>QueryCache; return yield* import EffectEffect.
const all: <{
    readonly assignee: Effect.Effect<QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>, never, IssueApi>;
    readonly issue: Loaded<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}, {
    ...;
}>(arg: {
    readonly assignee: Effect.Effect<QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>, never, IssueApi>;
    readonly issue: Loaded<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}, options?: {
    ...;
} | undefined) => Effect.Effect<...>
all
(
{
assignee: Effect.Effect<QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, never, IssueApi>
assignee
: const cache: QueryCache.QueryCachecache.
QueryCache.prefetch<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(factory: (input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, input: {
    issueId: number;
}): Effect.Effect<QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, never, IssueApi>
prefetch
(
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
, { issueId: numberissueId: id: numberid }),
issue: Loaded<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
issue
: const cache: QueryCache.QueryCachecache.
QueryCache.ensure<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(factory: (input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, input: {
    id: number;
}): Loaded<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>> (+1 overload)
ensure
(
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
, { id: numberid }),
}, { concurrency: "unbounded"concurrency: "unbounded" }, ); }, import EffectEffect.const catchTag: <"IssueNotFound", IssueNotFound | Unauthorized | CancelledError, never, NotFoundError, never, unassigned, never, never>(k: "IssueNotFound", f: (e: IssueNotFound) => Effect.Effect<never, NotFoundError, never>, orElse?: ((e: Unauthorized | CancelledError) => Effect.Effect<unassigned, never, never>) | undefined) => <A, R>(self: Effect.Effect<A, IssueNotFound | Unauthorized | CancelledError, R>) => Effect.Effect<...> (+1 overload)catchTag("IssueNotFound", () => import EffectEffect.const fail: <NotFoundError>(error: NotFoundError) => Effect.Effect<never, NotFoundError, never>fail(function notFound(options?: NotFoundError): NotFoundErrornotFound())), ); export const
const Route: Route<Register, RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", "/issues/$issueId", "/issues/$issueId", "/issues/$issueId", undefined, ResolveParams<"/issues/$issueId">, AnyContext, AnyContext, AnyContext, {}, (args: LoaderFnContext<Register, RootRoute<Register, undefined, ... 9 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, ... 4 more ..., undefined>
Route
=
createFileRoute<"/issues/$issueId", RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", "/issues/$issueId", "/issues/$issueId">(path?: "/issues/$issueId" | undefined): <TRegister, TSearchValidator, TParams, TRouteContextFn, TBeforeLoadFn, TLoaderDeps, TLoaderFn, TChildren, TSSR, TMiddlewares, THandlers>(options?: (ParamsOptions<...> & ... 1 more ... & UpdatableRouteOptions<...>) | undefined) => Route<...>
createFileRoute
("/issues/$issueId")({
FilebaseRouteOptionsInterface<Register, RootRoute<Register, undefined, { queryClient: QueryClient<IssueApi>; }, AnyContext, ... 7 more ..., undefined>, ... 12 more ..., undefined>.loader?: Constrain<(args: LoaderFnContext<Register, RootRoute<Register, undefined, ... 9 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, RouteLoaderFn<...> | RouteLoaderObject<...>> | undefinedloader: (
args: LoaderFnContext<Register, RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", ResolveParams<"/issues/$issueId">, {}, AnyContext, AnyContext, AnyContext, unknown, undefined>
args
) =>
args: LoaderFnContext<Register, RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", ResolveParams<"/issues/$issueId">, {}, AnyContext, AnyContext, AnyContext, unknown, undefined>
args
.
LoaderFnContext<Register, RootRoute<Register, undefined, { queryClient: QueryClient<IssueApi>; }, AnyContext, ... 7 more ..., undefined>, ... 7 more ..., undefined>.context: {
    queryClient: QueryClient<IssueApi>;
}
context
.queryClient.
QueryClient<IssueApi>.runPromise<{
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}, NotFoundError | ... 1 more ... | CancelledError>(effect: Effect.Effect<...>): Promise<...>
runPromise
(
const loadIssue: (id: number) => Effect.Effect<{
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}, NotFoundError | ... 1 more ... | CancelledError, IssueApi | QueryCache.QueryCache>
loadIssue
(
var Number: NumberConstructor
(value?: any) => number
Number
(
args: LoaderFnContext<Register, RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", ResolveParams<"/issues/$issueId">, {}, AnyContext, AnyContext, AnyContext, unknown, undefined>
args
.
LoaderFnContext<Register, RootRoute<Register, undefined, { queryClient: QueryClient<IssueApi>; }, AnyContext, ... 7 more ..., undefined>, ... 7 more ..., undefined>.params: {
    issueId: string;
}
params
.issueId: stringissueId))),
queryClient: QueryClient<IssueApi>
UpdatableRouteOptionsExtensions.component?: RouteComponent | undefinedcomponent: function IssuePage(): React.JSX.ElementIssuePage, UpdatableRouteOptionsExtensions.errorComponent?: false | ErrorRouteComponent | null | undefinederrorComponent: (props: ErrorComponentPropsprops) => ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.role?: React.AriaRole | undefinedrole="alert"> <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Couldn't load the issue.</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p> <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={props: ErrorComponentPropsprops.reset: () => voidreset}>Try again</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> ), }); function function IssuePage(): React.JSX.ElementIssuePage() { const
const loaderData: {
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}
loaderData
=
const Route: Route<Register, RootRoute<Register, undefined, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, unknown, unknown, unknown, unknown, undefined>, "/issues/$issueId", "/issues/$issueId", "/issues/$issueId", "/issues/$issueId", undefined, ResolveParams<"/issues/$issueId">, AnyContext, AnyContext, AnyContext, {}, (args: LoaderFnContext<Register, RootRoute<Register, undefined, ... 9 more ..., undefined>, ... 7 more ..., undefined>) => Promise<...>, ... 4 more ..., undefined>
Route
.
RouteExtensions<"/issues/$issueId", "/issues/$issueId">.useLoaderData: <RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>, unknown, boolean>(opts?: (UseLoaderDataBaseOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, ... 8 more ..., undefined>, "never", false, RouterHistory, Record<...>>, "/issues/$issueId", true, unknown, boolean> & OptionalStructuralSharing<...>) | undefined) => {
    ...;
}
useLoaderData
();
// Loader handled IssueNotFound; throw any other failure to errorComponent. const [const issue: Issueissue] =
useQuery<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, true>(factory: (input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, handle: QueryHandleWithData<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, options?: (Options & ... 1 more ... & {
    ...;
}) | undefined): readonly [data: ...] (+1 overload)
useQuery
(
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
,
const loaderData: {
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}
loaderData
.
issue: QueryHandleWithData<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
issue
, {
throwOnError?: true | undefinedthrowOnError: true, }); return ( <React.JSX.IntrinsicElements.article: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>article> <React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>{const issue: Issueissue.Issue.title: stringtitle}</React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1> <
function RenameForm(props: {
    issue: Issue;
}): React.JSX.Element
RenameForm
issue: Issueissue={const issue: Issueissue} />
<const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense React.SuspenseProps.fallback?: React.ReactNodefallback={<React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Loading assignee…</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>}> <
function Assignee(props: {
    handle: QueryHandle<typeof assigneeQuery>;
}): React.JSX.Element
Assignee
handle: QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
handle
={
const loaderData: {
    assignee: QueryHandle<(input: {
        issueId: number;
    }) => QueryOptions<Flat<{
        data: User;
        key: readonly ["issue", number, "assignee"];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
    issue: QueryHandleWithData<(input: {
        id: number;
    }) => QueryOptions<Flat<{
        data: Issue;
        key: readonly ["issue", number];
    } & {
        error: IssueNotFound | Unauthorized;
    } & {
        services: IssueApi;
    }>>>;
}
loaderData
.
assignee: QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
assignee
} />
</const Suspense: React.ExoticComponent<React.SuspenseProps>Suspense> </React.JSX.IntrinsicElements.article: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>article> ); } function
function Assignee(props: {
    handle: QueryHandle<typeof assigneeQuery>;
}): React.JSX.Element
Assignee
(
props: {
    handle: QueryHandle<typeof assigneeQuery>;
}
props
: {
handle: QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
handle
: interface QueryHandle<TFactory extends QueryFactory = (input: unknown) => FactoryResult>QueryHandle<typeof
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
> }) {
const [const assigneePromise: Thenable<Result.Result<User, IssueNotFound | Unauthorized>>assigneePromise,
const controls: QueryControls<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
controls
] =
useQuery<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, undefined>(factory: (input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, handle: QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, options?: (Options & ... 1 more ... & {
    ...;
}) | undefined): readonly [promise: ...] (+1 overload)
useQuery
(
function assigneeQuery(input: {
    issueId: number;
}): QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
assigneeQuery
,
props: {
    handle: QueryHandle<typeof assigneeQuery>;
}
props
.
handle: QueryHandle<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
handle
);
const assignee = use<Result.Result<User, IssueNotFound | Unauthorized>>(usable: React.Usable<Result.Result<User, IssueNotFound | Unauthorized>>): Result.Result<User, IssueNotFound | Unauthorized>use(const assigneePromise: Thenable<Result.Result<User, IssueNotFound | Unauthorized>>assigneePromise);
const assignee: Result.Result<User, IssueNotFound | Unauthorized>
return import ResultResult.
const match: <User, IssueNotFound | Unauthorized, React.JSX.Element, React.JSX.Element>(self: Result.Result<User, IssueNotFound | Unauthorized>, options: {
    readonly onFailure: (error: IssueNotFound | Unauthorized) => React.JSX.Element;
    readonly onSuccess: (ok: User) => React.JSX.Element;
}) => React.JSX.Element (+1 overload)
match
(const assignee: Result.Result<User, IssueNotFound | Unauthorized>assignee, {
onFailure: (error: IssueNotFound | Unauthorized) => React.JSX.ElementonFailure: (error: IssueNotFound | Unauthorizederror) => error: IssueNotFound | Unauthorizederror._tag: "IssueNotFound" | "Unauthorized"_tag === "IssueNotFound" ? ( <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Unassigned</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p> ) : ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.role?: React.AriaRole | undefinedrole="alert"> <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Couldn't load the assignee.</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p> <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() => void
const controls: QueryControls<(input: {
    issueId: number;
}) => QueryOptions<Flat<{
    data: User;
    key: readonly ["issue", number, "assignee"];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
controls
.QueryControls<(input: { issueId: number; }) => QueryOptions<Flat<{ data: User; key: readonly ["issue", number, "assignee"]; } & { error: IssueNotFound | Unauthorized; } & { ...; }>>>.refetch(): Promise<void>refetch()}>Try again</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> ), onSuccess: (ok: User) => React.JSX.ElementonSuccess: (user: Useruser) => <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>{user: Useruser.User.name: stringname}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>, }); }

With a layer, the client defaults to throwOnError: false: Effect useQuery returns a Result so you can match failures inline (Typed errors). Pass throwOnError: true on the call, or set it in the query's options, when you want a rejecting promise instead.

A list page that only prefetches never needs runPromise:

loader: (args) => {
	const qc = args.context.queryClient;
	return {
		counts: qc.prefetch(issueCountsQuery),
		issues: qc.prefetch(issueListQuery, args.deps),
	};
},

What the loader doesn't catch, like Unauthorized, rejects into errorComponent. The components never see the services: the cache runs each queryFn with the layer's, whoever starts it.

A form action

The action is an Effect.fn that renames and then waits for the refetched issue. It catches every failure rename declares into a message, so the Effect can't fail, and the form shows the message:

src/components/rename-form.tsx
import { import QueryCacheQueryCache } from "@raf-query/react";
import { 
function getRouteApi<const TId, TRouter extends AnyRouter = RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>(id: ConstrainLiteral<TId, RouteIds<TRouter["routeTree"]>>): RouteApi<TId, TRouter>
getRouteApi
} from "@tanstack/react-router";
import { import EffectEffect } from "effect"; import { function useActionState<State>(action: (state: Awaited<State>) => State | Promise<State>, initialState: Awaited<State>, permalink?: string): [state: Awaited<State>, dispatch: () => void, isPending: boolean] (+1 overload)useActionState } from "react"; import { type Issue, class IssueApiIssueApi } from "~/issues.ts"; const
const Route: RouteApi<"/issues/$issueId", RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>
Route
=
getRouteApi<"/issues/$issueId", RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>(id: ConstrainLiteral<"/issues/$issueId", "__root__" | "/issues/$issueId">): RouteApi<...>
getRouteApi
("/issues/$issueId");
const
const rename: (input: {
    readonly id: number;
    readonly title: string;
}) => Effect.Effect<string | null, never, IssueApi | QueryCache.QueryCache>
rename
= import EffectEffect.const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)fn("rename")(
function* (
input: {
    readonly id: number;
    readonly title: string;
}
input
: { readonly id: numberid: number; readonly title: stringtitle: string }) {
const
const api: {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}
api
= yield* class IssueApiIssueApi;
const const cache: QueryCache.QueryCachecache = yield* import QueryCacheQueryCache.const QueryCache: Service<QueryCache.QueryCache, QueryCache.QueryCache>QueryCache; yield*
const api: {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}
api
.
rename: (input: {
    readonly id: number;
    readonly title: string;
}) => Effect.Effect<Issue, EmptyTitle | Unauthorized>
rename
(
input: {
    readonly id: number;
    readonly title: string;
}
input
);
yield* const cache: QueryCache.QueryCachecache.QueryCache.invalidate<QueryFactory>(...target: QueryCache.Target<QueryFactory>): Effect.Effect<void>invalidate({ Filter.queryKey?: QueryKey | undefinedqueryKey: ["issue",
input: {
    readonly id: number;
    readonly title: string;
}
input
.id: numberid] });
return null; }, import EffectEffect.
const catchTags: <Unauthorized | EmptyTitle, {
    EmptyTitle: () => Effect.Effect<string, never, never>;
    Unauthorized: () => Effect.Effect<string, never, never>;
}, unassigned, never, never>(cases: {
    EmptyTitle: () => Effect.Effect<string, never, never>;
    Unauthorized: () => Effect.Effect<string, never, never>;
}, orElse?: ((e: never) => Effect.Effect<unassigned, never, never>) | undefined) => <A, R>(self: Effect.Effect<A, Unauthorized | EmptyTitle, R>) => Effect.Effect<...> (+1 overload)
catchTags
({
type EmptyTitle: () => Effect.Effect<string, never, never>EmptyTitle: () => import EffectEffect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>succeed("Title can't be empty"), type Unauthorized: () => Effect.Effect<string, never, never>Unauthorized: () => import EffectEffect.const succeed: <string>(value: string) => Effect.Effect<string, never, never>succeed("Sign in to rename issues"), }), ); export function
function RenameForm(props: {
    issue: Issue;
}): React.JSX.Element
RenameForm
(
props: {
    issue: Issue;
}
props
: { issue: Issueissue: Issue }) {
const const queryClient: QueryClient<IssueApi>queryClient =
const Route: RouteApi<"/issues/$issueId", RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>>
Route
.
RouteApi<"/issues/$issueId", RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient<IssueApi>; }, ... 8 more ..., undefined>, "never", false, RouterHistory, Record<...>>>.useRouteContext: <RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes, unknown, unknown, undefined>, "never", false, RouterHistory, Record<string, any>>, unknown>(opts?: UseRouteContextBaseOptions<RouterCore<Route<Register, any, "/", "/", string, "__root__", undefined, {}, {
    queryClient: QueryClient<IssueApi>;
}, ... 8 more ..., undefined>, "never", false, RouterHistory, Record<...>>, "/issues/$issueId", true, unknown> | undefined) => {
    ...;
}
useRouteContext
().queryClient: QueryClient<IssueApi>queryClient;
const [const message: string | nullmessage, const action: (payload: FormData) => voidaction, const isPending: booleanisPending] = useActionState<string | null, FormData>(action: (state: string | null, payload: FormData) => string | Promise<string | null> | null, initialState: string | null, permalink?: string): [state: string | null, dispatch: (payload: FormData) => void, isPending: boolean] (+1 overload)useActionState( (_previous: string | null_previous: string | null, formData: FormDataformData: FormData) => const queryClient: QueryClient<IssueApi>queryClient.QueryClient<IssueApi>.runPromise<string | null, never>(effect: Effect.Effect<string | null, never, IssueApi | QueryCache.QueryCache>): Promise<string | null>runPromise(
const rename: (input: {
    readonly id: number;
    readonly title: string;
}) => Effect.Effect<string | null, never, IssueApi | QueryCache.QueryCache>
rename
({ id: numberid:
props: {
    issue: Issue;
}
props
.issue: Issueissue.Issue.id: numberid, title: stringtitle:
var String: StringConstructor
(value?: any) => string
String
(formData: FormDataformData.FormData.get(name: string): FormDataEntryValue | nullget("title")) }),
), null, ); return ( <React.JSX.IntrinsicElements.form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>form React.FormHTMLAttributes<HTMLFormElement>.action?: string | ((formData: FormData) => void | Promise<void>) | undefinedaction={const action: (payload: FormData) => voidaction}> <React.JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>input React.HTMLAttributes<HTMLInputElement>.defaultValue?: string | number | readonly string[] | undefineddefaultValue={
props: {
    issue: Issue;
}
props
.issue: Issueissue.Issue.title: stringtitle} React.InputHTMLAttributes<HTMLInputElement>.name?: string | undefinedname="title" />
<React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefineddisabled={const isPending: booleanisPending}>Rename</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> {const message: string | nullmessage === null ? null : <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p React.HTMLAttributes<HTMLParagraphElement>.role?: React.AriaRole | undefinedrole="alert">{const message: stringmessage}</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>} </React.JSX.IntrinsicElements.form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>form> ); }

The client comes from the router context, not useQueryClient(): that one is a QueryClient<unknown>, so it can't check the action's services (Services). Typed errors covers where else to catch failures, and what React sees when you don't.

Effect queryFns

What an Effect queryFn adds over a promise:

  • Typed errors. The Effect's error type is the query's: qc.effect.fetch(issueQuery, input) fails with IssueNotFound | Unauthorized | Query.CancelledError. ErrorOf<TFactory> names it; it's unknown for promises. See Typed errors.
  • Services. What the Effect requires, ServicesOf<TFactory>, every verb that can start the query requires. See Services.
  • Defects aren't retried. A queryFn that throws instead of returning, or an Effect that dies, is a defect: it fails the request without retrying.
  • Interruption. Cancelling the request interrupts the fiber, so its finalizers run. No AbortSignal needed.
  • Schedules. retry accepts a Schedule as well as a count or predicate:
export function 
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
(
input: {
    id: number;
}
input
: { id: numberid: number }) {
return
queryOptions<Issue, IssueNotFound | Unauthorized, IssueApi, readonly ["issue", number], undefined>(options: Omit<QueryOptions<{
    data: Issue;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number];
    services: IssueApi;
}>, "kind" | "throwOnError"> & ThrowOnErrorField<undefined>): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
queryOptions
({
queryKey: readonly ["issue", number]queryKey: ["issue",
input: {
    id: number;
}
input
.id: numberid],
function queryFn(context: QueryFunctionContext): QueryFnResult<{
    data: Issue;
    error: IssueNotFound | Unauthorized;
    key: readonly ["issue", number];
    services: IssueApi;
}>
queryFn
: () => class IssueApiIssueApi.
Service<IssueApi, { readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>; readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>; readonly rename: (input: { readonly id: number; readonly title: string; }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>; }>.use<Issue, IssueNotFound | Unauthorized, never>(f: (service: {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}) => Effect.Effect<...>): Effect.Effect<...>
use
((
api: {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}
api
) =>
api: {
    readonly get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>;
    readonly assignee: (issueId: number) => Effect.Effect<User, IssueNotFound | Unauthorized>;
    readonly rename: (input: {
        readonly id: number;
        readonly title: string;
    }) => Effect.Effect<Issue, EmptyTitle | Unauthorized>;
}
api
.get: (id: number) => Effect.Effect<Issue, IssueNotFound | Unauthorized>get(
input: {
    id: number;
}
input
.id: numberid)),
retry?: RetryPolicy | undefinedretry: import ScheduleSchedule.const exponential: (base: Input, factor?: number) => Schedule.Schedule<Duration>exponential("200 millis").Pipeable.pipe<Schedule.Schedule<Duration, unknown, never, never>, Schedule.Schedule<Duration, unknown, never, never>>(this: Schedule.Schedule<Duration, unknown, never, never>, ab: (_: Schedule.Schedule<Duration, unknown, never, never>) => Schedule.Schedule<Duration, unknown, never, never>): Schedule.Schedule<Duration, unknown, never, never> (+21 overloads)pipe(import ScheduleSchedule.
const upTo: (options: {
    readonly duration?: Input | undefined;
    readonly times?: number | undefined;
}) => <Output, Input, Error, Env>(self: Schedule.Schedule<Output, Input, Error, Env>) => Schedule.Schedule<Output, Input, Error, Env> (+1 overload)
upTo
({ times?: number | undefinedtimes: 3 })),
}); }

The cache as an Effect service

QueryClient is a Promise wrapper around a QueryCache service. queryClient.runPromise hands it to loaders and actions, as above, and qc.effect is the same service as a value. Effect code outside React builds its own with QueryCache.make(config) (scoped: closing the scope interrupts every request and timer) or QueryCache.layer(config):

import { import QueryCacheQueryCache } from "@raf-query/core";
import { import StreamStream } from "effect";

const const program: Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, QueryCache.QueryCache | IssueApi>program = import EffectEffect.
const gen: <Effect.Effect<QueryCache.QueryCache, never, QueryCache.QueryCache> | Effect.Effect<QueryHandleWithData<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, CancelledError | IssueNotFound | Unauthorized, IssueApi> | Effect.Effect<void, never, IssueApi>, void>(f: () => Generator<...>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
const const cache: QueryCache.QueryCachecache = yield* import QueryCacheQueryCache.const QueryCache: Service<QueryCache.QueryCache, QueryCache.QueryCache>QueryCache; const
const issue: QueryHandleWithData<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
issue
= yield* const cache: QueryCache.QueryCachecache.
QueryCache.fetch<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(factory: (input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, input: {
    id: number;
}): Loaded<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>> (+1 overload)
fetch
(
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
, { id: numberid: 1 });
yield* const cache: QueryCache.QueryCachecache.
QueryCache.watch<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(factory: (input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, input: {
    id: number;
}): Stream.Stream<State<Issue>, never, IssueApi>
watch
(
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
, { id: numberid: 1 }).Pipeable.pipe<Stream.Stream<State<Issue>, never, IssueApi>, Stream.Stream<string | undefined, never, IssueApi>, Effect.Effect<void, never, IssueApi>>(this: Stream.Stream<State<Issue>, never, IssueApi>, ab: (_: Stream.Stream<State<Issue>, never, IssueApi>) => Stream.Stream<string | undefined, never, IssueApi>, bc: (_: Stream.Stream<string | undefined, never, IssueApi>) => Effect.Effect<void, never, IssueApi>): Effect.Effect<...> (+21 overloads)pipe(
import StreamStream.const map: <State<Issue>, string | undefined>(f: (a: State<Issue>, i: number) => string | undefined) => <E, R>(self: Stream.Stream<State<Issue>, E, R>) => Stream.Stream<string | undefined, E, R> (+1 overload)map((state: State<Issue>state) => state: State<Issue>state.State<Issue>.data: Issue | undefineddata?.Issue.title: string | undefinedtitle), import StreamStream.const runForEach: <string | undefined, void, never, never>(f: (a: string | undefined) => Effect.Effect<void, never, never>) => <E, R>(self: Stream.Stream<string | undefined, E, R>) => Effect.Effect<void, E, R> (+1 overload)runForEach((title: string | undefinedtitle) => import EffectEffect.const log: (...message: ReadonlyArray<any>) => Effect.Effect<void>log(title: string | undefinedtitle)), ); }); import EffectEffect.const runFork: <void, CancelledError | IssueNotFound | Unauthorized>(effect: Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, never>, options?: Effect.RunOptions | undefined) => Fiber<void, CancelledError | IssueNotFound | Unauthorized>runFork( const program: Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, QueryCache.QueryCache | IssueApi>program.Pipeable.pipe<Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, QueryCache.QueryCache | IssueApi>, Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, IssueApi>, Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, CancelledError | IssueNotFound | Unauthorized, QueryCache.QueryCache | IssueApi>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)pipe( import EffectEffect.
const provide: <QueryCache.QueryCache, never, never>(layer: Layer<QueryCache.QueryCache, never, never>, options?: {
    readonly local?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, QueryCache.QueryCache>> (+5 overloads)
provide
(import QueryCacheQueryCache.const layer: (config?: QueryCache.Config) => Layer<QueryCache.QueryCache>layer()),
import EffectEffect.
const provide: <IssueApi, never, never>(layer: Layer<IssueApi, never, never>, options?: {
    readonly local?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, IssueApi>> (+5 overloads)
provide
(const IssueApiLive: Layer<IssueApi, never, never>IssueApiLive),
), );

The cache has every verb as an Effect: prefetch, ensure, fetch, invalidate, reset, remove, setData, getData (an Option), isFetching (a count), dehydrate and hydrate. watch is a Stream of an entry's state, and events a Stream of cache events.

watch counts as a reader while it runs, so invalidation and window focus refetch the entry. Outside React, cache.mount (Effect<void, never, Scope>) refetches read, stale entries on window focus and reconnect while its scope is open.

Entries and observers

Entries are a data type with module functions, like Effect's own modules:

import { import QueryQuery, import QueryCacheQueryCache } from "@raf-query/core";

const const program: Effect.Effect<Query.State<Issue>, IssueNotFound | Unauthorized | CancelledError, QueryCache.QueryCache | IssueApi>program = import EffectEffect.
const gen: <Effect.Effect<QueryCache.QueryCache, never, QueryCache.QueryCache> | Effect.Effect<Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, never, IssueApi> | Effect.Effect<Issue, IssueNotFound | Unauthorized | CancelledError, IssueApi> | Effect.Effect<...>, Query.State<...>>(f: () => Generator<...>) => Effect.Effect<...> (+1 overload)
gen
(function* () {
const const cache: QueryCache.QueryCachecache = yield* import QueryCacheQueryCache.const QueryCache: Service<QueryCache.QueryCache, QueryCache.QueryCache>QueryCache; const
const query: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
query
= yield* const cache: QueryCache.QueryCachecache.
QueryCache.getQuery<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(factory: (input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>, input: {
    id: number;
}): Effect.Effect<Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, never, IssueApi>
getQuery
(
function issueQuery(input: {
    id: number;
}): QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>
issueQuery
, { id: numberid: 1 });
yield* import QueryQuery.
const fetch: <(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(self: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>, request?: Query.FetchRequest) => Effect.Effect<Issue, IssueNotFound | ... 1 more ... | CancelledError, IssueApi> (+1 overload)
fetch
(
const query: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
query
);
yield* import QueryQuery.
function invalidate<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(self: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>): Effect.Effect<void>
invalidate
(
const query: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
query
);
return import QueryQuery.
function getUnsafe<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>(self: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>): Query.State<Issue>
getUnsafe
(
const query: Query.Query<(input: {
    id: number;
}) => QueryOptions<Flat<{
    data: Issue;
    key: readonly ["issue", number];
} & {
    error: IssueNotFound | Unauthorized;
} & {
    services: IssueApi;
}>>>
query
); // synchronous read
});

Query.changes(query) streams the entry's state, and Query.subscribeUnsafe(query, listener) is the synchronous version.

QueryObserver is one reader's view of a query, as useQuery sees it: the controls and the promise, with whilePending, refetchInterval and the focus and reconnect options. QueryObserver.make(query, options) creates one, and QueryObserver.changes(observer) streams its results. While it runs, the query counts as read and polls on refetchInterval. It's the building block for bindings to other frameworks.

SSR streaming

The SSR integrations are thin wrappers over Effect APIs:

  • dehydrateStream(options) (scoped) returns the cache's initial state and a Stream of updates: entries that start or settle later, each sent once, batched per microtask. The stream ends when the scope closes.
  • hydrateStream(stream) hydrates each state as it arrives.
  • Stream.toReadableStream and Stream.fromReadableStream carry it over a web stream.

The sent option (a Set of hashes) makes separate dehydrate calls of one request skip entries already sent.

Time and the page

Time comes from Effect's Clock, and the page from the Platform service (focus, visibility, navigator.onLine; the default reads the globals). Provide TestClock or another Platform to test staleness, garbage collection and offline retries without a browser. See Testing.