Local cache

FFDB uses multiple cache layers for different goals. This guide explains what each layer is for, which ones persist, and how to choose the right storage strategy for your app.

Why multiple cache layers exist

One cache strategy does not fit every runtime or UX need.

  • Name
    In-memory query cache
    Description

    Optimizes repeated reads during a single runtime session. Fastest access, no restart persistence.

  • Name
    Storage adapter cache and auth state
    Description

    Persists session and related client state depending on adapter choice.

  • Name
    Offline local database cache
    Description

    Durable local data layer used by offline mode and sync workflows.

Storage adapter interface

type StorageAdapter = {
  get(key: string): Promise<string | null> | string | null
  set(key: string, value: string): Promise<void> | void
  remove(key: string): Promise<void> | void
}

This abstraction lets browser, desktop, mobile, and server apps use the same client API while plugging in runtime-specific persistence.

Built-in storage options

import { browserStorage, memoryStorage, createClient } from 'ffdb-client'

// Process-only cache/state. Resets on reload.
const memory = memoryStorage()

// Browser persistent options.
const local = browserStorage('local')
const session = browserStorage('session')
const indexed = browserStorage('indexeddb')

const client = await createClient({
  config: { apiUrl: import.meta.env.VITE_FFDB_API_URL },
  storage: local,
})

Choosing the right storage backend

  • Name
    memoryStorage()
    Description

    Best for tests and short-lived sessions. No persistence after reload or process restart.

  • Name
    browserStorage('local')
    Description

    Best default for browser apps that should survive tab reloads and browser restarts.

  • Name
    browserStorage('session')
    Description

    Useful when session should be isolated per tab.

  • Name
    browserStorage('indexeddb')
    Description

    Better when you prefer async storage semantics in browser contexts.

Query cache behavior

FFDB query hooks maintain an in-memory map of settled query results and in-flight promises. This reduces duplicate work during rerenders and concurrent reads.

type QueryCacheRecord = {
  data: unknown[]
  updatedAt: number
}

// Simplified behavior:
// - queryCache stores settled query results
// - inflightQueries deduplicates same-query concurrent requests
// - clearQueryCache clears both maps

End-to-end cache strategy example

This pattern balances persistence and freshness for browser apps.

import { browserStorage, createClient } from 'ffdb-client'
import type { Database } from './ffdb.types'

const { auth, sync, destroy } = await createClient<Database>({
  config: {
    apiUrl: import.meta.env.VITE_FFDB_API_URL,
  },
  storage: browserStorage('local'),
  offline: {
    adapter: offlineAdapter,
    syncOnReconnect: true,
    syncOnFocus: true,
  },
})

await auth.getSession()

if (sync) {
  await sync.run()
}

await destroy()

Practical guidance

  1. Use persistent storage when users expect to stay signed in across reloads.
  2. Treat in-memory query cache as performance-only, not a source of durable truth.
  3. Clear or refresh cached state after auth transitions and significant sync events.
  4. Keep storage choices explicit per runtime so behavior is predictable in web, mobile, and server contexts.

Next pages

  1. Sync lifecycle
  2. Mutation queue
  3. Conflict behavior

Was this page helpful?