Snippets
Minimal copy-paste blocks for flags, configs, kill switches, experiments and i18n.
Minimal copy-paste blocks, grouped by the registry taxonomy. These are the same leaves the docs get op returns.
release
release / flags
Read a feature flag per user with a bound Client. Assumes configure() ran at startup — see Installation.
import { Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
// construct once per callsite (cheap; binds the user + runs the attributes transform)
const flags = new Client(currentUser);
// getFlag(name, defaultValue?)
// name — the flag/gate name
// defaultValue — returned ONLY when the flag can't be evaluated
// (client not ready / flag not found); defaults to false
if (flags.getFlag("{{FLAG_KEY}}", false)) {
// ship it
}release / configs
Read a typed dynamic config (with a default when the key is absent). Assumes configure() ran at startup — see Installation.
import { Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
// construct once per callsite (cheap; binds the user)
const flags = new Client(currentUser);
// getConfig<T>(name, opts?)
// name — the config name
// opts.defaultValue — returned when the config key is absent
// opts.decode — optional (raw) => T to validate/shape the stored value
const cfg = flags.getConfig<{ max: number }>("{{CONFIG_KEY}}", {
defaultValue: { max: 50 }, // used when the key isn't published
decode: (raw) => raw as { max: number }, // optional — typed decode / zod parse
});release / killswitches
Read a kill switch (not user-bound — global on/off, optionally per-switch). Assumes configure() ran at startup — see Installation.
Whole kill switch
import { Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
// construct once per callsite (cheap; binds the user)
const flags = new Client(currentUser);
// getKillswitch(name, switchKey?)
// name — the kill switch name
// switchKey — optional; reads a single named override switch instead of
// the whole-killswitch "killed" flag
if (flags.getKillswitch("{{KILLSWITCH_KEY}}")) {
// killed — short-circuit the feature
}Named switch (with fallback)
const flags = new Client(currentUser); // construct once per callsite
// Pass the variable to gate as the switchKey. A CONFIGURED switch returns its
// own value; an UNCONFIGURED switch falls back to the whole-killswitch "killed"
// value — so this is always safe to call before any per-key override exists.
if (flags.getKillswitch("{{KILLSWITCH_KEY}}", "apple_pay")) {
// the "apple_pay" switch is on (or the whole kill switch is killed)
}release / experiments
Read an experiment's params, then record the conversion event on the same bound Client. Assumes configure() ran at startup — see Installation.
import { Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
// construct once per callsite (cheap; binds the user + runs the attributes transform)
const flags = new Client(currentUser);
// getExperiment<P>(name, defaultParams, decode?)
// name — the experiment name
// defaultParams — params returned when NOT enrolled (control / holdout); also
// defines the param shape P
// decode — optional (raw) => P to validate/shape the group's params
const { inExperiment, group, params } = flags.getExperiment(
"{{EXPERIMENT_KEY}}",
{ primary_label: "Sign up" }, // defaultParams (used when not enrolled)
);
render(params.primary_label);
// On conversion — Client-only track (NOT the Engine); the unit is inferred
// from the bound user (user_id, else anonymous_id):
// track(eventName, props?)
// eventName — the success event name
// props — optional metric properties (private attrs are stripped)
flags.track("{{SUCCESS_EVENT}}", { group }); // props optionalmetrics
metrics / track
Track a metric/conversion event from the bound Client. Metrics in the
dashboard are computed from these events. Assumes configure() ran at startup —
see Installation.
Track an event
import { Client } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
// construct once per callsite (cheap; binds the user)
const flags = new Client(currentUser);
// track(eventName, props?)
// eventName — the event your metric is built on (required)
// props — optional payload; numeric/string fields you can sum/filter on
// in a metric (private attributes are stripped before egress)
flags.track("{{EVENT_NAME}}", { amount: 49, currency: "usd" });Fire-and-forget (never blocks your response) and a no-op under
configureForTesting() / configureForOffline(). The unit is the bound user
(user_id, else anonymous_id); with no unit the call is a no-op.
Track without properties
const flags = new Client(currentUser); // construct once per callsite
flags.track("{{EVENT_NAME}}"); // props are optionali18n
i18n / setup
Wire the i18n loader via the SSR bootstrap (it rides the server shipeasy() handle — no separate i18n init). The loader serves the {{PROFILE}} profile and hydrates window.i18n. Full root-layout wiring is on the Installation page.
// app/layout.tsx — Next.js root layout (React Server Component)
import { shipeasy } from "@shipeasy/sdk/server";
// construct once per request (the SSR bootstrap handle; binds this request)
const se = await shipeasy({ serverKey: process.env.SHIPEASY_SERVER_KEY ?? "" });
// getBootstrapData(emit?)
// emit.clientKey — public client key embedded in the i18n loader tag (NOT
// the flags bootstrap tag); selects the {{PROFILE}} profile
const boot = se.getBootstrapData({
clientKey: process.env.NEXT_PUBLIC_SHIPEASY_CLIENT_KEY, // profile: {{PROFILE}}
});
// Render REAL <script> elements (dangerouslySetInnerHTML scripts do NOT run):
<script src={boot.bootstrap.src} {...boot.bootstrap.attrs} />;
{
boot.i18nLoader && <script src={boot.i18nLoader.src} {...boot.i18nLoader.attrs} />;
}i18n / render
Render a translated label with the i18n facade. The fallback is the source string; it shows until the loader resolves. Assumes the i18n loader is wired at startup — see Installation.
import { i18n } from "@shipeasy/sdk/client";
// t(key, fallback, variables?)
// key — the translation key
// fallback — source string shown until the loader resolves (and the
// extractable default)
// variables — optional {{var}} interpolation values
i18n.t("checkout.cta", "Place order");
i18n.t("cart.count", "{{count}} items", { count: cart.length }); // variablesops
ops / see
Report a caught, handled error (or a non-exception "violation") to Shipeasy with
see() — fire-and-forget, never re-throws. Package-level, so it reports against
the configuration from configure(). Assumes configure() ran at startup — see
Installation.
Report a handled exception
import { see } from "@shipeasy/sdk/server"; // or "@shipeasy/sdk/client"
try {
await charge(order);
} catch (e) {
// .causes_the(subject) what the error affects (e.g. "checkout")
// .to(outcome) the terminal — what you do about it; builds + fires once
see(e).causes_the("checkout").to("use the backup processor");
await fallbackCharge(order);
}Attach context with .extras(...)
try {
await charge(order);
} catch (e) {
// .extras(obj) structured fields attached to the report
see(e).causes_the("checkout").extras({ order_id: order.id }).to("use cached prices");
}Report a non-exception violation
// A bad state that isn't an exception — the name is a STABLE fingerprint; put
// variable data in .extras, never the name. .to() is the terminal.
see.Violation("missing_invoice").causes_the("billing").to("skip the dunning email");Mark an expected exception — report NOTHING
try {
parse(token);
} catch (e) {
// transmits nothing; .because(...) / .extras() are local-debug only
see.ControlFlowException(e).because("because end of stream is expected");
}