Node.js SDK
Node.js, JavaScript/TypeScript client for Reflag.com.
Reflag supports flag toggling, tracking flag usage, collecting feedback on features, and remotely configuring flags.
Installation
Install using your favorite package manager:
npm i @reflag/node-sdkyarn add @reflag/node-sdkbun add @reflag/node-sdkpnpm add @reflag/node-sdkdeno add npm:@reflag/node-sdkOther supported languages/frameworks are in the Supported languages documentation pages.
You can also use the HTTP API directly
Basic usage
To get started you need to obtain your secret key from the environment settings in Reflag.
Reflag will load settings through the various environment variables automatically (see Configuring below).
Find the Reflag secret key for your development environment under environment settings in Reflag.
Set
REFLAG_SECRET_KEYin your.envfileCreate a
reflag.tsfile containing the following:
import { ReflagClient } from "@reflag/node-sdk";
// Create a new instance of the client with the secret key. Additional options
// are available, such as supplying a logger and other custom properties.
//
// We recommend that only one global instance of `client` should be created
// to avoid multiple round-trips to our servers.
export const reflagClient = new ReflagClient();
// Initialize the client and begin fetching flag targeting definitions.
// You must call this method prior to any calls to `getFlags()`,
// otherwise an empty object will be returned.
reflagClient.initialize().then(() => {
console.log("Reflag initialized!");
});Once the client is initialized, you can obtain flags along with the isEnabled status to indicate whether the flag is targeted for this user/company:
If user.id is not given, the whole user object is ignore. Similarly, without company.id the company object is ignored.
You can also use the getFlags() method which returns a map of all flags:
High performance flag targeting
The SDK contacts the Reflag servers when you call initialize() 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 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
The SDK automatically batches operations like user/company updates and flag tracking events to minimize API calls. The batch buffer is configurable through the client options:
You can manually flush the batch buffer at any time:
It's recommended to call flush() before your application shuts down to ensure all events are sent.
Rate Limiting
The SDK includes automatic rate limiting for flag events to prevent overwhelming the API. Rate limiting is applied per unique combination of flag key and evaluation context. This behavior is built in and does not currently require configuration.
Flag definitions
Flag definitions include the rules needed to determine which flags should be enabled and which config values should be applied to any given user/company. Flag definitions are automatically fetched when calling initialize(). They are then cached and refreshed in the background. It's also possible to get the currently in use flag definitions:
Fallback provider
flagsFallbackProvider is a reliability feature that lets the SDK persist the latest successfully fetched raw flag definitions to fallback storage such as a local file, Redis, S3, GCS, or a custom backend.
fallbackFlags is deprecated. Prefer flagsFallbackProvider for startup fallback and outage recovery. flagsFallbackProvider is not used in offline mode.
How it works
Reflag servers remain the primary source of truth. On initialize(), the SDK always tries to fetch a live copy of the flag definitions first, and it continues refreshing those definitions from the Reflag servers over time.
If that initial live fetch fails, the SDK can call flagsFallbackProvider.load() and start with the last saved snapshot instead. This is mainly useful for cold starts in the exceedingly rare case that Reflag has an outage.
If Reflag becomes unavailable after the SDK has already initialized successfully, the SDK keeps using the last successfully fetched definitions it already has in memory. In other words, the fallback provider is mainly what helps future processes start, not what keeps an already running process alive.
After successfully fetching updated flag definitions, the SDK calls flagsFallbackProvider.save() to keep the stored snapshot up to date.
Typical reliability flow:
The SDK starts and tries to fetch live flag definitions from Reflag.
If that succeeds, those definitions are used immediately and the SDK continues operating normally.
After successfully fetching updated flag definitions, the SDK saves the latest snapshot through the fallback provider so a recent copy is available if needed later.
If a future process starts while Reflag is unavailable, it can load the last saved snapshot from the fallback provider and still initialize.
Once Reflag becomes available again, the SDK resumes using live data and refreshes the fallback snapshot.
Most deployments run multiple SDK processes, so more than one process may save identical flag definitions to the fallback storage at roughly the same time. This is expected and generally harmless for backends like a local file, Redis, S3, or GCS because the operation is cheap. In practice, this only becomes worth thinking about once you have many thousands of SDK processes writing to the same fallback storage.
If you are building a web or client-side application and want the most resilient setup, combine flagsFallbackProvider on the server with bootstrapped flags on the client.
flagsFallbackProvider helps new server processes start if they cannot reach Reflag during initialization. Bootstrapping helps clients render from server-provided flags instead of depending on an initial client-side fetch from the Reflag servers.
This applies to React (getFlagsForBootstrap() + ReflagBootstrappedProvider), the Browser SDK (bootstrappedFlags), and the Vue SDK (bootstrapped flags via the provider).
Built-in providers
You can access the built-in providers through the fallbackProviders namespace:
fallbackProviders.static(...)fallbackProviders.file(...)fallbackProviders.redis(...)fallbackProviders.s3(...)fallbackProviders.gcs(...)
Static provider
If you just want a fixed fallback copy of simple enabled/disabled flags, you can provide a static map:
File provider
The file provider stores one snapshot file per environment in the configured directory, using the filename flags-fallback-<secretKeyHash.slice(0, 16)>.json.
Redis provider
The built-in Redis provider creates a Redis client automatically when omitted and uses REDIS_URL from the environment. It stores snapshots under the configured keyPrefix and appends the first 16 characters of the secret key hash to that prefix.
Without a keyPrefix set, it will default to the key reflag:flags-fallback:<secretKeyHash.slice(0, 16)>. When you provide a custom keyPrefix, any trailing : is trimmed before the hash suffix is appended.
S3 provider
The built-in S3 provider works out of the box using the AWS SDK's default credential chain and region resolution. It stores the snapshot object under the configured keyPrefix and uses the filename flags-fallback-<secretKeyHash.slice(0, 16)>.json.
Without a keyPrefix set, it will default to the object key reflag/flags-fallback/flags-fallback-<secretKeyHash.slice(0, 16)>.json. When you provide a custom keyPrefix, any trailing / is trimmed before the filename is appended.
GCS provider
The built-in GCS provider works out of the box using Google Cloud's default application credentials. It stores the snapshot object under the configured keyPrefix and uses the filename flags-fallback-<secretKeyHash.slice(0, 16)>.json.
Without a keyPrefix set, it will default to the object key reflag/flags-fallback/flags-fallback-<secretKeyHash.slice(0, 16)>.json. When you provide a custom keyPrefix, any trailing / is trimmed before the filename is appended.
Testing fallback startup locally
To test fallback startup in your own app, first run it once with a working Reflag connection so a snapshot is saved. Then restart it with the same secret key and fallback provider configuration, but set apiBaseUrl (or set the REFLAG_API_BASE_URL environment variable) to http://127.0.0.1:65535. That forces the live fetch to fail and lets you verify that the SDK initializes from the saved snapshot instead.
Writing a custom provider
If you just store definitions in your database or similar, a custom provider can be very small:
Bootstrapping client-side applications
The getFlagsForBootstrap() method is useful whenever you need to pass flag data to another runtime or serialize it without wrapper functions. Server-side rendering (SSR) is a common example, but it is also useful for other bootstrapping and hydration flows.
You can also use a bound client for simpler API:
Key differences from getFlags()
getFlags()Raw data: Returns plain objects without
track()functions, making them JSON serializableContext included: Returns both the evaluated flags and the context used for evaluation
Bootstrapping focus: Designed specifically for passing data to client-side applications
Edge-runtimes like Cloudflare Workers
To use the Reflag NodeSDK with Cloudflare workers, set the node_compat flag in your wrangler file.
Instead of using ReflagClient, use EdgeClient and make sure you call ctx.waitUntil(reflag.flush()); before returning from your worker function.
See examples/cloudflare-worker for a deployable example.
Reflag maintains a cached set of flag definitions in the memory of your worker which it uses to decide which flags to turn on for which users/companies.
The SDK caches flag definitions in memory for fast performance. The first request to a new worker instance fetches definitions from Reflag's servers, while subsequent requests use the cache. When the cache expires, it's updated in the background. ctx.waitUntil(reflag.flush()) ensures completion of the background work, so response times are not affected. This background work may increase wall-clock time for your worker, but it will not measurably increase billable CPU time on platforms like Cloudflare.
EdgeClient uses flagsSyncMode: "in-request". Refresh fetch starts are throttled to at most once per second, and Cloudflare Workers cannot rely on delayed timer callbacks to run follow-up refreshes later. That means refreshFlags() calls made during the throttle window only mark a refresh as pending, so the call itself may resolve before the fetch runs. The queued refresh runs on the next request/access or refreshFlags() call after the throttle window expires.
Error Handling
The SDK is designed to fail gracefully and never throw exceptions to the caller. Instead, it logs errors and provides fallback behavior:
Flag Evaluation Failures:
Network Errors:
Missing Context:
Offline Mode:
The SDK logs all errors with appropriate severity levels. You can customize logging by providing your own logger:
Remote config
Remote config is a dynamic and flexible approach to configuring flag behavior outside of your app – without needing to re-deploy it.
Similar to isEnabled, each flag has a config property. This configuration is managed from within Reflag. It is managed similar to the way access to flags is managed, but instead of the binary isEnabled you can have multiple configuration values which are given to different user/companies.
key is mandatory for a config, but if a flag has no config or no config value was matched against the context, the key will be undefined. Make sure to check against this case when trying to use the configuration in your application. payload is an optional JSON value for arbitrary configuration needs.
Just as isEnabled, accessing config on the object returned by getFlags does not automatically generate a check event, contrary to the config property on the object returned by getFlag.
Configuring
The Reflag Node.js SDK can be configured through environment variables, a configuration file on disk or by passing options to the ReflagClient constructor. By default, the SDK searches for reflag.config.json in the current working directory.
secretKey
string
The secret key used for authentication with Reflag's servers.
REFLAG_SECRET_KEY
logLevel
string
The log level for the SDK (e.g., "DEBUG", "INFO", "WARN", "ERROR"). Default: INFO
REFLAG_LOG_LEVEL
offline
boolean
Operate in offline mode. Default: false, except in tests it will default to true based off of the TEST env. var. In offline mode the SDK does not fetch from Reflag and does not use flagsFallbackProvider.
REFLAG_OFFLINE
apiBaseUrl
string
The base API URL for the Reflag servers.
REFLAG_API_BASE_URL
flagOverrides
Record<string, boolean>
An object specifying flag overrides for testing or local development. See 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. 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
REFLAG_FLAGS_ENABLED and REFLAG_FLAGS_DISABLED are comma separated lists of flags which will be enabled or disabled respectively.
reflag.config.json example:
When using a reflag.config.json for local development, make sure you add it to your .gitignore file. You can also set these options directly in the ReflagClient constructor. The precedence for configuration options is as follows, listed in the order of importance:
Options passed along to the constructor directly,
Environment variable,
The config file.
Type safe flags
To get type checked flags, install the Reflag CLI:
then generate the types:
This will generate a reflag.d.ts containing all your flags. Any flag look ups will now be checked against the flags that exist in Reflag.
Here's an example of a failed type check:

This is an example of a failed config payload check:

Testing with flag overrides
When writing tests that cover code with flags, you can toggle flags on/off programmatically to test different behavior. For tests, you will often want to run the client in offline mode:
reflag.ts:
There are a few ways to programmatically manipulate the overrides which are appropriate when testing:
Base overrides
You can set base overrides for a test run by passing flagOverrides in the constructor, replacing them later with setFlagOverrides() and clearing them with clearFlagOverrides():
app.test.ts:
Layering overrides
pushFlagOverrides() serves a different purpose: it adds a temporary layer on top of the base overrides and returns a remove function that removes only that layer. This is useful for nested tests:
The precedence is:
Base overrides from the constructor or
setFlagOverrides()Temporary layers added by
pushFlagOverrides()
If the same flag is set in both places, the pushed override wins until its remove function is called.
Context dependent overrides
setFlagOverrides() and pushFlagOverrides() also accept a function if the override depends on the evaluation context:
Additional ways to provide flag overrides
You also have these additional ways to provide overrides, which can be helpful when testing out locally:
Through environment variables:
Through
reflag.config.json:
Remote Flag Evaluation
In addition to local flag evaluation, Reflag supports remote evaluation using stored context. This is useful when you want to evaluate flags using user/company attributes that were previously sent to Reflag:
Remote evaluation is particularly useful when:
You want to use the most up-to-date user/company attributes stored in Reflag
You don't want to pass all context attributes with every evaluation
You need to ensure consistent flag evaluation across different services
Using with Express
A popular way to integrate the Reflag Node.js SDK is through an express middleware.
See examples/express/app.ts for a full example.
Remote flag evaluation with stored context
If you don't want to provide context each time when evaluating flags but rather you would like to utilize the attributes you sent to Reflag previously (by calling updateCompany and updateUser) you can do so by calling getFlagsRemote (or getFlagRemote for a specific flag) with just userId and companyId in that order. These methods will call Reflag's servers and flags will be evaluated remotely using the stored attributes.
User and company attribute updates are processed asynchronously, so there might be a small delay between when attributes are updated and when they are available for evaluation.
Opting out of tracking
There are use cases in which you not want to be sending user, company and track events to Reflag.com. These are usually cases where you could be impersonating another user in the system and do not want to interfere with the data being collected by Reflag.
To disable tracking, bind the client using bindClient() as follows:
Another way way to disable tracking without employing a bound client is to call getFlag() or getFlags() by supplying enableTracking: false in the arguments passed to these functions.
Note, however, that calling track(), updateCompany() or updateUser() in the ReflagClient will still send tracking data. As such, it is always recommended to use bindClient() when using this SDK.
Flushing
ReflagClient employs a batching technique to minimize the number of calls that are sent to Reflag's servers.
By default, the SDK automatically subscribes to process exit signals and attempts to flush any pending events. This behavior is controlled by the flushOnExit option in the client configuration:
Tracking custom events and setting custom attributes
Tracking allows events and updating user/company attributes in Reflag. For example, if a customer changes their plan, you'll want Reflag to know about it, in order to continue to provide up-do-date targeting information in the Reflag interface.
The following example shows how to register a new user, associate it with a company and finally update the plan they are on.
It's also possible to achieve the same through a bound client in the following manner:
Some attributes are used by Reflag to improve the UI, and are recommended to provide for easier navigation:
name-- display name foruser/company,email-- the email of the user,avatar-- the URL foruser/companyavatar image.
Attributes cannot be nested (multiple levels) and must be either strings, integers or booleans.
Managing Last seen
Last seenBy default updateUser/updateCompany calls automatically update the given user/company Last seen property on Reflag servers.
You can control if Last seen should be updated when the events are sent by setting meta.active = false. This is often useful if you have a background job that goes through a set of companies just to update their attributes but not their activity.
Example:
bindClient() updates attributes on the Reflag servers but does not automatically update Last seen.
Zero PII
The Reflag SDK doesn't collect any metadata and HTTP IP addresses are not being stored. For tracking individual users, we recommend using something like database ID as userId, as it's unique and doesn't include any PII (personal identifiable information). If, however, you're using e.g. email address as userId, but prefer not to send any PII to Reflag, you can hash the sensitive data before sending it to Reflag:
Migrating from Bucket SDK
If you have been using the Bucket SDKs previously, the following list will help you migrate to Reflag SDK:
Bucket*classes, and types have been renamed toReflag*(e.g.BucketClientis nowReflagClient)Feature*classes, and types have been renamed toFlag*(e.g.Featureis nowFlag,RawFeaturesis nowRawFlags)When using strongly-typed flags, the new
Flagsinterface replacedFeaturesinterfaceAll methods that contained
featurein the name have been renamed to use theflagterminology (e.g.getFeatureisgetFlag)All environment variables that were prefixed with
BUCKET_are now prefixed withREFLAG_The
BUCKET_HOSTenvironment variable andhostoption have been removed fromReflagClientconstructor, useREFLAG_API_BASE_URLinsteadThe
BUCKET_FEATURES_ENABLEDandBUCKET_FEATURES_DISABLEDhave been renamed toREFLAG_FLAGS_ENABLEDandREFLAG_FLAGS_DISABLEDThe default configuration file has been renamed from
bucketConfig.jsontoreflag.config.jsonThe
fallbackFeaturesproperty in client constructor and configuration files has been renamed tofallbackFlagsfeatureKeyhas been renamed toflagKeyin all methods that accepts that argumentThe SDKs will not emit
evaluateandevaluate-configevents anymore
Typescript
Types are bundled together with the library and exposed automatically when importing through a package manager.
License
MIT License Copyright (c) 2025 Bucket ApS
Last updated
Was this helpful?