API reference

yatta/frontend

Query cache, realtime state, and environment detection.

11 exported symbols and 42 members, read from src/types/frontend.ts.

Construct

cacheKey

function

A cache key from a route name and its arguments, so calls agree on identity.

cacheKey(name: string, args?: unknown): string

connectRealtime

function

Wraps a so its state is observable.

Takes the connection as an argument rather than making one, so the frontend and the transport are the same object. Two connections would mean two topics and two sets of events, and neither would be the one the server is pushing to.

connectRealtime(client: RealtimeClient, options?: { queueWhileOffline?: boolean; }): RealtimeHandle

createFrontend

function

Builds the client-side surface.

createFrontend<const R extends Record<string, RouteDef>>(source: R | App<any>, options?: FrontendOptions<R>): Frontend<R>
tsts
import { createFrontend } from "yatta/frontend";import { routes } from "./api-contract"; export const frontend = createFrontend(routes, {  baseUrl: "/api",  realtime: createRealtimeClient({ url: "/realtime" }),  cookieName: "session",});

QueryCache

class

A small query cache: deduplicate in flight, share one value per key.

Not a general-purpose cache and not trying to be. It exists so two components asking for the same thing share one request and one value, and so a refetch does not blank the screen.

11 members

  • entriesproperty
    entries:
  • inFlightproperty
    inFlight:
  • subscribe
    subscribe(onChange: (changedKey?: string) => void, key?: string): () => void

    Subscribes to changes.

    onChange
    Told on every change, or — when a key is given — only when that key changes. Keyed is what a bound call uses: an unkeyed subscription wakes every mounted component when any one of them writes, so fifty calls on a page each re-render on every keystroke anywhere.
    key
    Limit to this key. Omit to hear about everything.
  • watchersproperty
    watchers:
  • notify
    notify(changedKey?: string): void
  • get
    get<T>(key: string): CacheEntry<T>
  • resolve
    resolve<T>(key: string, fetcher: () => Promise<T>, options?: { force?: boolean; }): Promise<T | undefined>

    Reads a key, starting a load if there is nothing fresh.

    A second caller for a key already loading gets the same promise, so ten components mounting at once cause one request.

  • set
    set<T>(key: string, updater: T | ((previous: T | undefined) => T)): void

    Writes a value directly, without a request.

  • invalidate
    invalidate(key?: string, all?: boolean): void

    Marks keys stale so the next read refetches.

    key
    Exact key, or a prefix ending in `:` — `"getUser:"` invalidates every call to that route whatever its arguments. A mutation on a user has to invalidate `getUser` for every id, and listing each one means writing down every id that was ever fetched.
    all
    Invalidate everything. Separate from `key`, because `undefined` means "this key" and cannot also mean "all of them".
  • clear
    clear(key?: string, all?: boolean): void

    Clears one key, or everything.

    A cache is only per-request state, so there is no TTL sweep here: on the server the whole instance is thrown away with the render, and in the browser a reload starts empty.

  • evict
    evict(): void

    Drops the oldest entries past the cap, so the cache cannot grow forever.

Types

CacheEntry

interface

One entry in the query cache.

interface CacheEntry<T = unknown>

5 members

  • dataproperty
    data: T | undefined

    The value, or `undefined` while the first load is still in flight.

  • errorproperty
    error: CallError | Error | undefined
  • fetchingproperty
    fetching: boolean

    Whether a request is in flight right now.

  • updatedAtproperty
    updatedAt: number | undefined

    When the data was last written, as epoch milliseconds.

  • staleproperty
    stale: boolean

    Whether the value is from a previous request rather than this one.

    Distinct from `fetching` on its own: a refetch that keeps the old data on screen means the UI can keep showing it instead of flashing empty.

CacheOptions

interface
interface CacheOptions

2 members

  • staleTimeproperty
    staleTime?: number

    How long a value stays fresh, in milliseconds. Defaults to 30 seconds.

  • maxEntriesproperty
    maxEntries?: number

    Upper bound on stored entries. Defaults to 200.

Frontend

interface

The whole client-side surface, in one object.

Built from the same route table the server serves, so `client` is typed from the schemas the server validates with.

interface Frontend<R extends Record<string, RouteDef>>

7 members

  • apiproperty
    api: ReturnType<typeof createClient<R>>

    Typed API methods, one per route.

  • cacheproperty
    cache: QueryCache

    Query cache shared by every consumer of this frontend.

  • realtimeproperty
    realtime: RealtimeHandle | undefined

    Realtime, when a connection was supplied.

  • interactiveproperty
    interactive: boolean

    Whether this instance can open connections.

    `false` during a server render. Hooks use it to decide between "load it" and "wait for the browser", which is the difference between a working static render and a hydration mismatch.

  • getCookieproperty
    getCookie?: (name: string) => string | undefined

    Reads a session cookie value. `undefined` on the server.

  • setHeadersproperty
    setHeaders?: (headers: Headers) => void

    Sends on every request, for auth headers and the like.

  • dispose
    dispose(): void

    Releases timers and connections.

FrontendOptions

interface
interface FrontendOptions<R extends Record<string, RouteDef>>

5 members

  • realtimeproperty
    realtime?: RealtimeClient

    A realtime connection to observe. One to serve both, not two.

  • queueWhileOfflineproperty
    queueWhileOffline?: boolean

    Queue sends made while disconnected. Defaults to `true`.

  • cacheproperty
    cache?: CacheOptions
  • cookieNameproperty
    cookieName?: string

    The session cookie name, read on the server and sent by the browser.

    Read rather than accepted as a value, so a server render and the browser agree on who the user is without the app threading a token through.

  • interactiveproperty
    interactive?: boolean

    Overrides the environment check. Set false in tests.

QueryResult

interface

Read-only view of one cache entry, as a hook receives it.

interface QueryResult<T>

6 members

  • dataproperty
    data: T | undefined
  • errorproperty
    error: CallError | Error | undefined
  • fetchingproperty
    fetching: boolean
  • staleproperty
    stale: boolean

    The value is from a previous request; a new one is in flight.

  • refetchproperty
    refetch: () => Promise<T | undefined>
  • setproperty
    set: (updater: T | ((previous: T | undefined) => T)) => void

    Write a value without a request. For optimistic updates.

RealtimeHandle

interface
interface RealtimeHandle

6 members

  • statusproperty
    status: ConnectionStatus

    Current status. Read it at any time; it is kept up to date.

  • onStatus
    onStatus(onChange: (status: ConnectionStatus) => void): () => void

    Told on every status change. Returns an unsubscribe function.

  • on
    on<T = unknown>(event: string, handler: (data: T) => void): () => void

    Listens for one event.

    Returns an unsubscribe function, so it pairs with a component's teardown.

  • join
    join(topic: string): () => void

    Joins a topic. Returns the function that leaves it.

  • send
    send(event: string, data: unknown): Promise<void>

    Sends an event. Queued while disconnected rather than thrown.

  • queuedproperty
    queued: number

    Forgets queued sends. Called when the connection goes away.

ConnectionStatus

type

Realtime connection state, as observable values.

The transport holds this privately; a UI needs to read it, and re-deriving it in each component is how two components end up disagreeing about whether the app is online.

type ConnectionStatus = "connecting" | "open" | "closed"
Tip
Most of the types above are inferred. You rarely import AuthConfig or JobPayload — declaring your schema once is enough for the rest to follow. See Typed keys.