From 339b7f87d079904eddac7a5f08a13a68e2ea9363 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Sun, 19 Apr 2026 13:32:18 +0200 Subject: [PATCH] Make push the default node-sdk sync mode --- .changeset/node-sdk-push-default.md | 7 +++++++ .../openfeature-node-provider-push-default.md | 5 +++++ packages/node-sdk/README.md | 10 +++++----- packages/node-sdk/examples/express/reflag.ts | 1 - packages/node-sdk/src/client.ts | 8 ++++++-- packages/node-sdk/src/types.ts | 4 ++-- packages/node-sdk/test/client.test.ts | 19 +++++++++++++++++++ 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 .changeset/node-sdk-push-default.md create mode 100644 .changeset/openfeature-node-provider-push-default.md diff --git a/.changeset/node-sdk-push-default.md b/.changeset/node-sdk-push-default.md new file mode 100644 index 00000000..77825c44 --- /dev/null +++ b/.changeset/node-sdk-push-default.md @@ -0,0 +1,7 @@ +--- +"@reflag/node-sdk": minor +--- + +Change the default `flagsSyncMode` from `"polling"` to `"push"`. + +New `ReflagClient` instances now subscribe to live SSE flag updates by default unless `flagsSyncMode` is set explicitly. The deprecated `cacheStrategy` option still maps `"periodically-update"` to `"polling"` and `"in-request"` to `"in-request"`. diff --git a/.changeset/openfeature-node-provider-push-default.md b/.changeset/openfeature-node-provider-push-default.md new file mode 100644 index 00000000..fc8f5b22 --- /dev/null +++ b/.changeset/openfeature-node-provider-push-default.md @@ -0,0 +1,5 @@ +--- +"@reflag/openfeature-node-provider": minor +--- + +Updated to `@reflag/node-sdk` `1.6.0`, which defaults to `flagsSyncMode="push"`. diff --git a/packages/node-sdk/README.md b/packages/node-sdk/README.md index 051ac585..953f990e 100644 --- a/packages/node-sdk/README.md +++ b/packages/node-sdk/README.md @@ -142,10 +142,10 @@ and downloads the flags with their targeting rules. These rules are then matched against the user/company information you provide to `getFlags()` (or through `bindClient(..).getFlags()`). That means the `getFlags()` call does not need to contact the Reflag servers once -`initialize()` has completed. By default, `ReflagClient` will continue to -refresh the targeting rules from the Reflag servers in the background. You can -change this behavior with `flagsSyncMode` to use request-driven refreshes or -push-based updates instead. +`initialize()` has completed. By default, `ReflagClient` uses +`flagsSyncMode: "push"`, which keeps targeting rules up to date via live SSE +updates. You can switch `flagsSyncMode` to `polling` for periodic background +refreshes or `in-request` for request-driven refreshes instead. ### Batch Operations @@ -569,7 +569,7 @@ current working directory. | `apiBaseUrl` | string | The base API URL for the Reflag servers. | REFLAG_API_BASE_URL | | `flagOverrides` | Record | An object specifying flag overrides for testing or local development. See [examples/express/app.test.ts](https://github.com/reflagcom/javascript/tree/main/packages/node-sdk/examples/express/app.test.ts) for how to use `flagOverrides` in tests. | REFLAG_FLAGS_ENABLED, REFLAG_FLAGS_DISABLED | | `flagsFallbackProvider` | `FlagsFallbackProvider` | Optional provider used to load and save raw flag definitions for fallback startup when the initial live fetch fails. Available only through the constructor. Ignored in offline mode. | - | -| `flagsSyncMode` | `"polling" \| "in-request" \| "push"` | Flag-definition sync mode. `polling` uses periodic background refresh, `in-request` refreshes stale flags during request handling, and `push` subscribes to live updates. Default: `"polling"`. | - | +| `flagsSyncMode` | `"polling" \| "in-request" \| "push"` | Flag-definition sync mode. `push` subscribes to live updates, `polling` uses periodic background refresh, and `in-request` refreshes stale flags during request handling. Default: `"push"`. | - | | `flagsPushUrl` | string | Push endpoint used when `flagsSyncMode: "push"`. Default: `https://pubsub.reflag.com/sse`. | - | | `configFile` | string | Load this config file from disk. Default: `reflag.config.json` | REFLAG_CONFIG_FILE | diff --git a/packages/node-sdk/examples/express/reflag.ts b/packages/node-sdk/examples/express/reflag.ts index 87d78ce6..f754bbe9 100644 --- a/packages/node-sdk/examples/express/reflag.ts +++ b/packages/node-sdk/examples/express/reflag.ts @@ -40,5 +40,4 @@ export default new ReflagClient({ // Optional: Set a logger to log debug information, errors, etc. // logger: console, flagOverrides, // Optional: Set flag overrides - flagsSyncMode: "push", }); diff --git a/packages/node-sdk/src/client.ts b/packages/node-sdk/src/client.ts index 89735ba2..3c73fc8c 100644 --- a/packages/node-sdk/src/client.ts +++ b/packages/node-sdk/src/client.ts @@ -309,7 +309,7 @@ export class ReflagClient { * @param options.configFile - The path to the config file (optional). * @param options.flagsFetchRetries - Number of retries for fetching flags (optional, defaults to 3). * @param options.fetchTimeoutMs - Timeout for fetching flags (optional, defaults to 10000ms). - * @param options.flagsSyncMode - How flag definitions are synchronized (optional, defaults to "polling"). + * @param options.flagsSyncMode - How flag definitions are synchronized (optional, defaults to "push"). * * @throws An error if the options are invalid. **/ @@ -462,7 +462,11 @@ export class ReflagClient { const flagsSyncMode: FlagsSyncMode = options.flagsSyncMode ?? - (options.cacheStrategy === "in-request" ? "in-request" : "polling"); + (options.cacheStrategy === "in-request" + ? "in-request" + : options.cacheStrategy === "periodically-update" + ? "polling" + : "push"); const secretKeyHash = config.secretKey ? hashString(config.secretKey) : ""; const secretKeyHashPrefix = secretKeyHash.slice(0, 16); diff --git a/packages/node-sdk/src/types.ts b/packages/node-sdk/src/types.ts index 328285d9..8cf46124 100644 --- a/packages/node-sdk/src/types.ts +++ b/packages/node-sdk/src/types.ts @@ -709,9 +709,9 @@ export type ClientOptions = { /** * How flag definitions are synchronized. * - * - `polling` (default): periodic background refresh. + * - `push` (default): live updates over SSE keep flag definitions up to date. + * - `polling`: periodic background refresh. * - `in-request`: stale refresh is triggered during request handling. - * - `push`: live updates over SSE keep flag definitions up to date. */ flagsSyncMode?: FlagsSyncMode; diff --git a/packages/node-sdk/test/client.test.ts b/packages/node-sdk/test/client.test.ts index e2cf65cb..ca767f28 100644 --- a/packages/node-sdk/test/client.test.ts +++ b/packages/node-sdk/test/client.test.ts @@ -91,6 +91,7 @@ const validOptions: ClientOptions = { intervalMs: 10001, flushOnExit: false, }, + flagsSyncMode: "polling", offline: false, }; @@ -314,6 +315,10 @@ describe("ReflagClient", () => { expect(client["_config"].apiBaseUrl).toBe(API_BASE_URL); expect(client["_config"].refetchInterval).toBe(FLAGS_REFETCH_MS); + expect(client["_config"].flagsSyncMode).toBe("push"); + expect(client["_config"].flagsPushUrl).toBe( + "https://pubsub.reflag.com/sse?channels=flags-state%3A165d2650f1975f7f", + ); expect(client.httpClient).toBe(fetchClient); expect(client["_config"].headers).toEqual(expectedHeaders); expect(client["_config"].fallbackFlags).toBeUndefined(); @@ -323,6 +328,20 @@ describe("ReflagClient", () => { }); }); + it("should map deprecated cacheStrategy values to sync modes", () => { + const inRequestClient = new ReflagClient({ + secretKey: "validSecretKeyWithMoreThan22Chars", + cacheStrategy: "in-request", + }); + const pollingClient = new ReflagClient({ + secretKey: "validSecretKeyWithMoreThan22Chars", + cacheStrategy: "periodically-update", + }); + + expect(inRequestClient["_config"].flagsSyncMode).toBe("in-request"); + expect(pollingClient["_config"].flagsSyncMode).toBe("polling"); + }); + it("should throw an error if options are invalid", () => { let invalidOptions: any = null; expect(() => new ReflagClient(invalidOptions)).toThrow(