@ts-pf/swr
v0.1.3
Published
Opt-in SWR keys, fetchers, mutators, and matchers
Downloads
946
Maintainers
Readme
@ts-pf/swr
Opt-in SWR helpers for a ts-pf client. Typed keys, fetchers, mutators, matchers, and stream subscribers — you still call useSWR yourself.
Agent skill: skills/ts-pf-swr/. Sync with npx skills experimental_sync -y.
This package does not wrap useSWR, add a React provider, switch RPC to GET, or fold into @ts-pf/client. Pass the client from createClient<typeof contract>(link) or createClient<Contract>(link) with a generated Contract from @ts-pf/codegen.
Setup
import { createClient } from '@ts-pf/client'
import { FetchLink } from '@ts-pf/client-http'
import { createSwr } from '@ts-pf/swr'
import type { contract } from './contract'
const client = createClient<typeof contract>(new FetchLink({ url: '/rpc' }))
export const swr = createSwr(client)Pass { prefix: 'user' } when two utils trees would otherwise share keys.
Data fetching
import useSWR from 'swr'
const { data, error, isLoading } = useSWR(
swr.planet.find.key({ input: { id: 123 } }),
swr.planet.find.fetcher(),
)SWR’s error is the thrown PFError. error.code still narrows declared errors; isLocalFailure(error) is local === true. Fetchers do not wrap asResult.
Skip a request with a null key: id ? swr.planet.find.key({ input: { id } }) : null.
Infinite queries
import useSWRInfinite from 'swr/infinite'
const { data, size, setSize } = useSWRInfinite(
(index, previousPage) => {
if (previousPage && !previousPage.nextCursor) {
return null
}
return swr.planet.list.key({
input: { cursor: previousPage?.nextCursor },
})
},
swr.planet.list.fetcher(),
)The fetcher reads input from the key, so each page can pass a different cursor.
Mutations
import useSWRMutation from 'swr/mutation'
const { trigger, isMutating } = useSWRMutation(
swr.planet.list.key(),
swr.planet.create.mutator(),
)
trigger({ name: 'New Planet' })The useSWRMutation key is the cache entry to revalidate (often a list). trigger is the mutating procedure’s input.
Manual revalidation
import { mutate } from 'swr'
mutate(swr.matcher())
mutate(swr.planet.matcher())
mutate(
swr.planet.find.matcher({ input: { id: 123 }, strategy: 'exact' }),
)Default matcher strategy is 'partial' (path prefix + nested input subset).
Subscriptions
For procedures whose output is AsyncIterable (typically stream()):
import useSWRSubscription from 'swr/subscription'
const { data, error } = useSWRSubscription(
swr.planet.describe.key({ input: { id: 3 } }),
swr.planet.describe.subscriber({ maxChunks: 10 }),
)liveSubscriber() keeps the latest event instead of an array. Unsubscribe aborts the call via CallOptions.signal.
Calling clients
The procedure utils object is callable, and .call is the underlying client:
const planet = await swr.planet.find({ id: 123 })
const same = await swr.planet.find.call({ id: 123 })Not in this package
- TanStack Query
- GET vs POST / client operation context
- Wrapping
useSWRasswr.planet.find.useSWR(input) - Retry (
RetryPluginoncreateClient) - React components or an
SWRConfigprovider
