effect-api-query builds typed TanStack Query options and cache keys from Effect RPC and HttpApi contracts.
Pass these options to QueryClient or your framework's Query hooks. The contract defines the inputs and results.
Your application controls transport, client lifetime, and cache policy.
Install the package and TanStack Query Core:
pnpm add @ueberbrot/effect-api-query @tanstack/query-coreThe package requires one exact Effect 4 release because its RPC and HTTP API modules are unstable.
If automatic peer installation is enabled, pnpm installs that version. Existing applications must
use the same version. Use TypeScript 5.9 or newer with strict: true, ESM, and ES2022 support. See
compatibility and stability.
Import the shared contract in both your server and client. Before calling readUser, acquire a matching flat RPC client.
Keep its Scope alive while you use the client. The
RPC quick start
shows acquisition and cleanup.
Shared contract in rpc-contract.ts:
import { Schema } from 'effect'
import { Rpc, RpcGroup } from 'effect/rpc'
export const usersRpc = RpcGroup.make(
Rpc.make('users.get', {
payload: { id: Schema.Int },
success: Schema.Struct({ id: Schema.Int, name: Schema.String }),
}),
)Client integration in client.ts:
import { QueryClient } from '@tanstack/query-core'
import { createRpcQueryUtils } from '@ueberbrot/effect-api-query'
import type { RpcClient, RpcGroup } from 'effect/rpc'
import { usersRpc } from './rpc-contract.ts'
async function readUser(
client: RpcClient.RpcClient.Flat<RpcGroup.Rpcs<typeof usersRpc>>,
queryClient: QueryClient,
) {
const rpc = createRpcQueryUtils(usersRpc, { client, keyPrefix: ['users-app'] })
const user = await queryClient.query(rpc.users.get.queryOptions({ input: { id: 1 } }))
return user.name
}Dotted tags become nested utility paths. For example, users.get becomes rpc.users.get.
RPC inputs use the payload Schema's constructor input, including fields that its defaults can supply.
Import the shared HttpApi in both your server and client. Before calling readUser, acquire its ready HttpApiClient.
Keep its transport resources alive while you use the client. The
HTTP quick start
shows acquisition and cleanup.
Shared contract in http-contract.ts:
import { Schema } from 'effect'
import { HttpApi, HttpApiEndpoint, HttpApiGroup } from 'effect/http-api'
export const usersApi = HttpApi.make('users-api').add(
HttpApiGroup.make('users').add(
HttpApiEndpoint.get('get', '/users/:id', {
params: { id: Schema.Int },
success: Schema.Struct({ id: Schema.Int, name: Schema.String }),
}),
),
)Client integration in client.ts:
import { QueryClient } from '@tanstack/query-core'
import { createHttpApiQueryUtils } from '@ueberbrot/effect-api-query'
import type { HttpApiClient } from 'effect/http-api'
import { usersApi } from './http-contract.ts'
async function readUser(client: HttpApiClient.ForApi<typeof usersApi>, queryClient: QueryClient) {
const http = createHttpApiQueryUtils(usersApi, { client, keyPrefix: ['users-app'] })
const user = await queryClient.query(
http.users.get.queryOptions({ input: { params: { id: 1 } } }),
)
return user.name
}HTTP inputs use decoded params, query, headers, and payload containers. Both examples use
the default Effect runner. If execution needs services, provide a signal-aware runPromiseExit.
Choose a keyPrefix that identifies your application and any user or tenant that affects the returned data.
Keep credentials out of keys.
| Contract | Generated operations |
|---|---|
| Unary RPC or buffered nonmultipart HTTP | Query, infinite query, mutation |
| Buffered nonmultipart HTTP | Separate view of response status and headers |
| Buffered multipart HTTP | Mutation with explicit FormData |
| RPC stream or unambiguous HTTP SSE | Accumulated streamed query, live query |
Use generated keys to read, write, and invalidate cache entries. Queries forward cancellation to
Effect. Use withQueryPolicy and withMutationPolicy to share typed defaults.
Your application decides which queries to invalidate after mutations. See
feature support
for supported contracts and limits.
- Documentation: queries, mutations, pagination, streaming, policies, and caching.
- Integrations: framework setup and client lifetime.
- Examples: complete Vite React and TanStack Start applications.
- API reference: factories, builders, keys, and errors.
Install the library usage skill from this repository:
npx skills add ueberBrot/effect-api-query --full-depth --skill effect-api-queryThe npm package includes the same skill. After installing the library, run
npx @tanstack/intent@latest install and enable @ueberbrot/effect-api-query, then load it with:
npx @tanstack/intent@latest load @ueberbrot/effect-api-query#effect-api-queryIntent loads the skill from your installed library version. Update a skill installed from GitHub through the Skills CLI.