Skip to Content
ReferenceClient APIDownsyncAdapter

DownsyncAdapter

Interface for fetching initial snapshots and delta updates from server.

Import

import type { DownsyncAdapter } from '@gluonic/client'

Interface Definition

Exact definition from implementation:

export type DownsyncAdapter = { fetchDelta: ( since: number, token: string, onUnauthenticated: () => void | Promise<void> ) => Promise<DeltaResult> fetchBootstrap?: ( token: string, onUnauthenticated: () => void | Promise<void> ) => Promise<BootstrapResult> }

Total: 2 methods (1 required, 1 optional). fetchBootstrap is highly recommended for initial sync.


Methods

fetchDelta()

fetchDelta( since: number, token: string, onUnauthenticated: () => void | Promise<void> ): Promise<DeltaResult>

Fetch changes since a specific sync ID (delta sync).

Parameters:

ParameterTypeDescription
sincenumberLast sync ID client has
tokenstringJWT auth token
onUnauthenticated() => void | Promise<void>Callback if 401/403

Returns: Promise<DeltaResult> - Delta frames or rows

Example:

const delta = await downsyncAdapter.fetchDelta( 1000, // Last sync ID token, () => router.push('/login') ) // Apply delta to store await store.applyFrames(delta.frames || [])

fetchBootstrap() (Optional)

fetchBootstrap?( token: string, onUnauthenticated: () => void | Promise<void> ): Promise<BootstrapResult>

Fetch complete initial snapshot (bootstrap).

Parameters:

ParameterTypeDescription
tokenstringJWT auth token
onUnauthenticated() => void | Promise<void>Callback if 401/403

Returns: Promise<BootstrapResult> - Complete snapshot with sync ID

Example:

const snapshot = await downsyncAdapter.fetchBootstrap( token, () => router.push('/login') ) // Apply bootstrap to store await store.applyMany(snapshot.rows) await storage.setLastSyncId(snapshot.sid)

Recommended: Always implement for initial sync. Fallback to fetchDelta(0, token) if missing.


Default Implementation

Default HTTP-based downsync adapter:

const defaultDownsyncAdapter: DownsyncAdapter = { async fetchDelta(since, token, onUnauthenticated) { const response = await fetch( `/sync/v1/delta?since=${since}`, { headers: { 'Authorization': `Bearer ${token}` } } ) if (response.status === 401 || response.status === 403) { await onUnauthenticated() throw new Error('Unauthenticated') } if (!response.ok) { throw new Error(`Delta sync failed: ${response.statusText}`) } return await response.json() }, async fetchBootstrap(token, onUnauthenticated) { const response = await fetch( '/sync/v1/bootstrap', { headers: { 'Authorization': `Bearer ${token}` } } ) if (response.status === 401 || response.status === 403) { await onUnauthenticated() throw new Error('Unauthenticated') } if (!response.ok) { throw new Error(`Bootstrap failed: ${response.statusText}`) } return await response.json() } }

Custom Implementation

With Compression

import type { DownsyncAdapter } from '@gluonic/client' import { decompress } from 'compression-library' class CompressedDownsyncAdapter implements DownsyncAdapter { async fetchDelta(since, token, onUnauthenticated) { const response = await fetch(`/sync/v1/delta?since=${since}`, { headers: { 'Authorization': `Bearer ${token}`, 'Accept-Encoding': 'gzip' } }) if (response.status === 401) { await onUnauthenticated() throw new Error('Unauthenticated') } const compressed = await response.arrayBuffer() const json = await decompress(compressed) return JSON.parse(json) } async fetchBootstrap(token, onUnauthenticated) { // Similar with compression } }

With Retry Logic

class RetryDownsyncAdapter implements DownsyncAdapter { async fetchDelta(since, token, onUnauthenticated) { let attempts = 0 const maxAttempts = 3 while (attempts < maxAttempts) { try { const response = await fetch(`/sync/v1/delta?since=${since}`, { headers: { 'Authorization': `Bearer ${token}` } }) if (response.ok) { return await response.json() } if (response.status === 401 || response.status === 403) { await onUnauthenticated() throw new Error('Unauthenticated') } } catch (err) { attempts++ if (attempts >= maxAttempts) throw err // Exponential backoff await new Promise(r => setTimeout(r, Math.pow(2, attempts) * 1000)) } } } }

Usage in Gluonic

DownsyncAdapter is used by Store for:

Initial Bootstrap

// First app launch (lastSyncId = 0) if (lastSyncId === 0 && downsyncAdapter.fetchBootstrap) { const snapshot = await downsyncAdapter.fetchBootstrap(token, onUnauthenticated) await store.applyMany(snapshot.rows, { persist: true }) await storage.setLastSyncId(snapshot.sid) }

Delta Sync

// Subsequent syncs (catch up) const delta = await downsyncAdapter.fetchDelta(lastSyncId, token, onUnauthenticated) if (delta.frames) { await store.applyFrames(delta.frames) } else if (delta.rows) { await store.applyMany(delta.rows, { persist: true }) } await storage.setLastSyncId(delta.sid)

Configuration

const client = SyncClient({ server: 'https://api.example.com/sync/v1', storage, models, downsync: { fetchDelta: async (since, token, onUnauthenticated) => { // Custom delta sync }, fetchBootstrap: async (token, onUnauthenticated) => { // Custom bootstrap } } })

Note: If not provided, Gluonic creates default HTTP adapter from server URL.


See Also

Last updated on