Automatic connection
SSE is exclusive to the server entry point and is always on — there is no option to enable or disable it. The browser entry never uses SSE; it receives updates via native polling (pollIntervalMs).
init() always starts with a snapshot request that loads the current flags. Real-time updates depend on the entry point:
- Browser (
@canarygate/sdk/client): never uses SSE. Flags are loaded via a snapshot request (/sdk/flags) and refreshed automatically by polling everypollIntervalMs(default30000, clamped to a3000ms minimum,0disables). This prevents thousands of open SSE connections coming from browsers. - Server (
@canarygate/sdk/server, for Node.js, Deno, Bun, Edge): always opens an SSE connection after the snapshot. Flags are then updated in the background without polling.
// Server, real-time: snapshot + SSE stream (always on)
import { CanaryGate } from '@canarygate/sdk/server'
const realtimeClient = new CanaryGate('cg_key')
await realtimeClient.init()
// From this point on, realtimeClient.getFlag() always returns
// the latest value without needing a manual refetchIf the server entry is used where a window exists (browser), the constructor throws; import from @canarygate/sdk/client instead. Likewise, the client entry throws if used in an environment without window.
Reacting to changes (React)
For React components to reflect real-time flag changes, you need a reactivity mechanism. The SDK does not include native React bindings, but it is straightforward to implement:
import { CanaryGate } from '@canarygate/sdk/client'
import { useState, useEffect, useRef } from 'react'
function useFlagEnabled(client: CanaryGate, key: string): boolean {
const [enabled, setEnabled] = useState(
() => client.getFlag(key)?.enabled ?? false
)
useEffect(() => {
const interval = setInterval(() => {
const current = client.getFlag(key)?.enabled ?? false
setEnabled(current)
}, 500)
return () => clearInterval(interval)
}, [client, key])
return enabled
}On the server the SSE stream is always on; in browsers prefer the SDK’s
built-in polling (pollIntervalMs). Since the SDK already polls internally,
the setInterval in the example above is not strictly necessary — but it is
safe to keep.
isStale()
Returns true when the last flags sync failed or the stream connection is down (for example, while reconnecting). Use it to detect a degraded state.
if (client.isStale()) {
// Data may be stale
// Consider reloading or showing a warning to the user
}Use isStale() to monitor connection health in critical dashboards.
getLastSyncAt()
Returns the ISO timestamp (string | null) of the last successful sync, or null if init() has not completed yet.
const lastSync = client.getLastSyncAt()
if (lastSync) {
console.log('Flags updated', Date.now() - new Date(lastSync).getTime(), 'ms ago')
}disconnect()
Closes the stream connection (if any) and releases resources. Always call this on teardown:
// React:
useEffect(() => {
return () => client.disconnect()
}, [])
// Node.js / script:
process.on('SIGTERM', () => client.disconnect())After disconnect(), getFlag() continues to return the last known values, but will no longer receive updates.