RenderContextDocumentation

Server SDK

Evaluate feature flags and experiments from your backend with @rendercontext/server. Use the same raw viewerId as the browser SDK so assignments and analytics join for signed-in users. By default the SDK polls config about once a minute and evaluates locally — exposures and custom events still go to the ingest API.

1. Credentials

In the console, open your project → Environments → edit an environment. Copy the public key and generate a server secret key (sk_live_…). Store the secret in your backend secrets manager — never ship it to the browser.

2. Install and init

npm install @rendercontext/server

import { createClient } from "@rendercontext/server";

const rc = createClient({
  publicKey: process.env.RC_PUBLIC_KEY!,
  secretKey: process.env.RC_SECRET_KEY!,
});

3. Evaluate flags

Pass the raw app user id (same value as browser setViewerId). The SDK hashes it with your environment public key.

const { flags } = await rc.evaluate({
  viewerId: user.id,
  flagKeys: ["image_model"],
});

const model = flags.image_model === "v2" ? "v2" : "v1";

Target with backend context

Pass cohort and attribute values derived from your authenticated backend session or database. This secret-keyed path trusts those values directly; a viewer-context token is only needed when sending attributes to the browser SDK. Unknown or archived cohorts are ignored.

const { flags } = await rc.evaluate({
  viewerId: user.id,
  flagKeys: ["new_checkout"],
  context: {
    cohorts: user.cohortKeys,
    attributes: { plan: user.plan, role: user.role },
  },
});

4. Experiments and exposure

evaluate does not record experiment exposures. When the variant affects the response, call exposure explicitly (no browser exposureToken — the secret authenticates the request).

const { experiments } = await rc.evaluate({
  viewerId: user.id,
  experimentKeys: ["image_model_test"],
});

const assignment = experiments.image_model_test;
if (assignment) {
  // …use assignment.variant…
  await rc.exposure({
    viewerId: user.id,
    experimentKey: "image_model_test",
    variant: assignment.variant,
  });
}

5. Track events

await rc.event({
  viewerId: user.id,
  name: "image_generated",
});

Limitations

  • Country targeting is not applied on server evaluate (no Cloudflare country on the request). Prefer flags/experiments without country rules for server-only decisions, or evaluate those in the browser.
  • Flag/experiment config is polled in-process by default (60s). Pass configPollIntervalMs: 0 to skip background polling (evaluate still loads config when needed). If config cannot be fetched, evaluate throws. The browser SDK cannot poll full defs — it stays on the edge evaluate path.
  • Sensitive behaviour (billing, admin, costly models) must still be enforced on your server — flags are not an authorization boundary.

Related

Back to docs