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:
| Parameter | Type | Description |
|---|---|---|
since | number | Last sync ID client has |
token | string | JWT 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:
| Parameter | Type | Description |
|---|---|---|
token | string | JWT 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
- DeltaResult - Return type from fetchDelta
- BootstrapResult - Return type from fetchBootstrap
- SyncFrame - Frame format in delta
- Store.bootstrap() - Uses fetchBootstrap
- Store.fastForward() - Uses fetchDelta
- Delta Sync Concept - How delta sync works
Last updated on