Node.js SDK

Node.js feature flags for your backend

The Node SDK keeps a streamed copy of your ruleset in memory, so an isEnabled check is a local, sub-millisecond call in your request path. Flag a new query path, a new dependency, or a whole endpoint, and turn it off in seconds if it misbehaves.

No card required

01 · Drop it in

The planned Node.js API, exactly as it will ship

The SDK is part of the early-access program, and this is its real surface: one install, one client, one call per decision. Every toggle, rollout step and kill-switch flip you make in the console reaches this code in about 3 seconds, with no redeploy.

Works in Express, Fastify, NestJS, queue workers and cron jobs; TypeScript types included.

npm install @toggled/node
node.js · checkout.new-flow live
import { Toggled } from '@toggled/node';

const flags = new Toggled({ sdkKey: process.env.TOGGLED_SDK_KEY });

app.post('/checkout', async (req, res) => {
  const newFlow = await flags.isEnabled('checkout.new-flow', {
    userId: req.user.id,
    country: req.user.country,
    plan: req.user.plan,
  });

  return newFlow ? newCheckout(req, res) : classicCheckout(req, res);
});

02 · Built for Node.js

What Node.js feature flags get you

In-process evaluation

Rules stream to the SDK and evaluate locally. Your hot path never blocks on our API, and a burst of traffic costs you nothing extra.

Fail-safe by design

If the stream drops, the SDK keeps serving the last-known ruleset and your fallback values. An outage on our side never becomes an outage on yours.

Consistent hashing across services

The same userId lands in the same rollout cohort in every service and every SDK, so a 10% backend rollout matches the 10% your frontend shows.

03 · Then the usual lifecycle

Flag it, target it, roll it out, measure, kill

Once checkout.new-flow exists in your Node.js code, everything else happens in the console: target the internal team, run a gradual rollout from 1% to 100%, read the impact with built-in A/B testing, and keep the kill switch under your thumb. The full walkthrough is on how Toggled works.

Flat pricing applies here too: unlimited seats and unlimited flags on every plan, so the whole Node.js team gets an account. Numbers are on the pricing page.

Same flags, other stacks

Cohorts agree across SDKs: a user in the 10% on your Node.js side is in the same 10% everywhere else.

Java, Rails, iOS and Android SDKs share the same core and ship with early access.

04 · Where the check goes

Where a flag check belongs in a Node.js codebase

01

At the edge of the handler, not deep in a service

Read the flag where the request context still exists, then pass the boolean (or the strategy you picked) inward. Services that receive a dependency instead of reading a global stay unit-testable, and you avoid threading a user object down through five layers just so layer five can call isEnabled.

02

As a strategy swap, not an if per call site

For a rewritten query path or a second payment provider, resolve the flag once and choose an implementation behind the same interface. The call sites do not change, and removing the flag later means deleting one branch inside one factory function.

03

In middleware for whole-endpoint gates

Gating an entire route, a beta endpoint or a new API version, belongs in an Express or Fastify middleware that reads the flag and returns 404 when it is off. The handler underneath then contains no flag logic at all and can be tested as if the gate did not exist.

05 · Gotchas

Three ways Node.js feature flags go wrong

Gotcha 01

A new client per request

Constructing the client inside a handler means every request builds a client, fetches the ruleset and throws it away, which is precisely the round trip that local evaluation exists to remove. Create it once at module scope or register it as a singleton in your container, and inject that one instance everywhere.

Gotcha 02

Reading flags before the first ruleset arrives

At boot there is a short window where the client holds no rules and every check returns your fallback. If the server is already accepting traffic in that window, the first requests silently take the OFF path. Await the client ready promise before you bind the port, and keep the fallbacks honest anyway.

Gotcha 03

The queue worker never got the flag

The API reads the flag, the consumer does not, so a job enqueued by the new code path gets processed by the old handler. Either put the decision in the job payload, or evaluate the same key in the consumer with the same user id so both sides land in the same cohort.

06 · Testing

Testing both paths in Node.js

Inject the client instead of importing it, then hand your handlers a fake backed by a plain object in Jest or Vitest. Cover three cases: flag on, flag off, and a client that throws, which is the one that proves your fallback actually holds when the SDK is unhappy. Supertest against the route is enough, and CI never needs a live connection.

node.js · test both paths
// checkout.test.js (Vitest + Supertest)
import request from 'supertest';
import { buildApp } from './app.js';

const fakeFlags = (values) => ({
  isEnabled: async (key) => values[key] ?? false,
});

test('serves the new flow when the flag is on', async () => {
  const app = buildApp({ flags: fakeFlags({ 'checkout.new-flow': true }) });
  const res = await request(app).post('/checkout');

  expect(res.body.flow).toBe('new');
});

test('falls back to classic when the SDK throws', async () => {
  const app = buildApp({
    flags: { isEnabled: async () => { throw new Error('offline'); } },
  });
  const res = await request(app).post('/checkout');

  expect(res.body.flow).toBe('classic');
});

07 · People also ask

Node.js feature flag questions, answered

How do you use feature flags in a Node.js API?

Create one client when the process boots, hold it for the lifetime of that process, and call isEnabled inside your handlers with the request user context. The client keeps the ruleset in memory and evaluates locally, so a check inside a route handler is a function call rather than an outbound HTTP request.

What does in-process flag evaluation actually mean?

The SDK receives your targeting rules once, keeps them in memory, and computes each decision locally. Nothing leaves the process per evaluation, so a check costs microseconds and adds no new failure mode to the request path. Rule changes arrive on a background stream and swap the in-memory ruleset when they land.

How do feature flags work across Node cluster workers or PM2 processes?

Each worker process gets its own client and its own copy of the ruleset, and each receives updates independently. Cohorts still agree because the rollout bucket is a hash of the user key, not per-process state, so a given user falls on the same side of a 10% rollout in every worker.

Do feature flags slow down a Node.js request?

Not when evaluation is local: the check is a map lookup plus a hash, comfortably under a millisecond, so it is safe in a hot path or inside a loop. An SDK that calls a vendor API per evaluation is a different proposition, because it puts that vendor latency and uptime inside every request you serve.

More answers, about the product rather than the code, live on the FAQ.

Early access · launching soon

Put your first Node.js feature behind a flag

Join the early-access list and be first in line when spots open. Flat pricing, unlimited seats, no card required.

No card required · your data stays yours