Remote Adapter
RemoteAdapter keeps dismissal/read state synchronized through your backend API.
Use it when users switch devices and you need consistent state.
Typical integration
featuredrop.config.ts
import { RemoteAdapter } from 'featuredrop/adapters'
export const storage = new RemoteAdapter({
url: '/api/featuredrop',
userId: 'current-user-id',
fetchInterval: 300_000,
requestTimeoutMs: 10_000,
retryAttempts: 3,
retryBaseDelayMs: 300,
retryOnStatuses: [408, 429, 500, 502, 503, 504],
circuitBreakerThreshold: 5,
circuitBreakerCooldownMs: 60_000,
dismissBatchWindowMs: 150,
syncOnOnline: true,
syncOnVisibilityChange: true,
syncIntervalMs: 60_000,
onError: (error, context) => {
console.warn('featuredrop remote error', context.operation, context.attempt, error)
}
})Expected endpoints
GET /api/featuredrop(manifest, optional if you fetch manifest separately)GET /api/featuredrop/state->{ watermark, dismissedIds }POST /api/featuredrop/dismisswith{ featureId }POST /api/featuredrop/dismiss-batchwith{ featureIds }(optional but recommended)POST /api/featuredrop/dismiss-allwith{ watermark }
Operational notes
- Adapter retries failed requests with exponential backoff.
- Parallel
fetchManifest()/syncState()calls are coalesced to a single in-flight request. - Circuit breaker opens after repeated failures to avoid request storms.
- Rapid dismiss interactions are batched by
dismissBatchWindowMs. - Failed queued dismisses are retained and retried on subsequent flushes.
- Enable
syncOnOnline/syncOnVisibilityChangewhen you want automatic recovery sync after reconnect or tab return. - Use
await storage.isHealthy()for diagnostics. - Use
await storage.flushPendingDismisses()before route unloads if you need aggressive durability. - Call
await storage.destroy()during teardown tests to remove listeners/timers.
⚠️
Keep backend route latency predictable and return explicit non-2xx on failures so retry logic behaves correctly.