Generated module clients
Game features are modules. The client SDKs do not
know any module; instead, the rudder CLI reads the manifests of the modules
installed in an environment and writes one typed client file for your game.
Install the CLI
Section titled “Install the CLI”The rudder CLI (0.1.0) ships as binaries for macOS, Linux and Windows
(amd64 and arm64) on the Gitea releases page of the
liveops-module-sdk repository.
Download the binary for your platform and put it on your PATH.
Generate
Section titled “Generate”export RUDDER_URL=https://api.rudder.buildexport RUDDER_TOKEN=... # personal access token (Account Settings in the dashboard)export RUDDER_PROJECT=... # project id
rudder client generate --lang ts --out src/rudder.modules.tsrudder client generate --lang csharp --out Assets/Rudder/RudderModules.g.cs| Flag | Meaning |
|---|---|
--lang ts|csharp | Output language. |
--out <file> | Output path. Defaults to rudder.modules.ts or RudderModules.g.cs. |
--environment staging|prod | Which environment’s installed modules to read. Default staging. |
--manifests <dir> | Read <dir>/*.json and <dir>/*/dist/manifest.json instead of the server. |
- Only functions with trigger
clientare generated. - The file header lists every module with its version.
- The file is a factory over the SDK client; nothing is patched into the SDK.
- Commit the file and regenerate it after installing, upgrading or removing modules. Do not edit it by hand. There is no editor integration: Unity projects run the CLI too.
import { createClient } from '@rudder/sdk';import { modules } from './rudder.modules';
const client = createClient({ projectKey });await client.auth.loginWithDevice();
const m = modules(client);await m.leaderboards.submit({ slug: 'weekly', score: 120 });const { entries } = await m.leaderboards.top({ slug: 'weekly', limit: 10 });The file exports:
modules(client):{ <module>: { <fn>(args): Promise<Result> } }, with module and function names in camelCase (remote_config→remoteConfig);<Module><Fn>Argsand<Module><Fn>Resultinterfaces (nested types are named by path) and string literal unions for enums;<Module><Fn>Error: the function’s declared error codes plusRudderKernelError.
using RudderSdk;using RudderSdk.Modules;
var m = new RudderModules(client);await m.Leaderboards.SubmitAsync(new LeaderboardsSubmitArgs { Slug = "weekly", Score = 120 });var top = await m.Leaderboards.TopAsync(new LeaderboardsTopArgs { Slug = "weekly", Limit = 10 });The file (namespace RudderSdk.Modules, netstandard2.1, Newtonsoft.Json)
contains:
RudderModuleswith one property per module (RemoteConfig,Leaderboards, …) and<Fn>Async(args, cancellationToken)methods;- plain
<Module><Fn>Argsand<Module><Fn>Resultclasses; optional properties are nullable and left out of the request when null; - constants classes for string enums and
<Module><Fn>Errorsfor error codes.
What a call does
Section titled “What a call does”Each generated function calls the SDK’s modules.call /
Modules.CallAsync, which sends POST /sdk/v1/modules/{module}/{fn} with
{ args } and returns result.
- Idempotency. The SDK generates one
Idempotency-Keyper call and sends the same key on every retry. The backend stores the response for 24 hours and returns it for a repeated key, so a retried call runs at most once. A key reused for another module or function fails with 422idempotency_key_mismatch. - Retries. Up to 2, with jittered backoff, on network errors, 5xx and
409
conflict. Other 4xx responses are never retried. - Version header. Generated clients send
X-Rudder-Module-Version: <module>@<version>with the version the file was generated from. When the major version differs from the installed one, the call still runs and the backend writes a warning to the module log (Modules → Logs). Regenerate the client when you see it.
Errors
Section titled “Errors”A module that fails a call on purpose returns HTTP 422
{ error, code, requestId }. The SDKs turn it into a typed error:
import { RudderModuleError } from '@rudder/sdk';import { modules, type StoreBuyError } from './rudder.modules';
try { await modules(client).store.buy({ offerSlug: 'starter_pack' });} catch (error) { if (error instanceof RudderModuleError) { switch (error.code as StoreBuyError) { case 'insufficient_funds': showNotEnoughFunds(); break; case 'purchase_limit_reached': showSoldOut(); break; } }}try{ await m.Store.BuyAsync(new StoreBuyArgs { OfferSlug = "starter_pack" });}catch (RudderModuleException e) when (e.Code == StoreBuyErrors.InsufficientFunds){ ShowNotEnoughFunds();}catch (RudderModuleException e) when (e.Code == StoreBuyErrors.PurchaseLimitReached){ ShowSoldOut();}The error sets come from the module: codes declared with rudder.Errors(...)
(see Module manifest) plus the
kernel codes such as invalid_parameters, forbidden, conflict and
insufficient_funds. A module crash is a 500 and a timeout a 504, both
without details.
remote_config helpers
Section titled “remote_config helpers”remote_config.get returns { values: { [key]: { type, value } } }, where
type is string, number, bool or json and value is the value as
text. When remote_config is installed, the generated file adds helpers that
parse a value by key and return nothing for a missing key or another type:
import { modules, remoteConfigBool, remoteConfigJson, remoteConfigNumber } from './rudder.modules';
const config = await modules(client).remoteConfig.get({});const multiplier = remoteConfigNumber(config, 'reward_multiplier') ?? 1;const newShop = remoteConfigBool(config, 'new_shop_enabled') ?? false;const tuning = remoteConfigJson<{ spawnRate: number }>(config, 'tuning');Helpers: remoteConfigString, remoteConfigNumber, remoteConfigBool,
remoteConfigJson<T>; each returns undefined when the key is missing or has
another type.
var config = await m.RemoteConfig.GetAsync(new RemoteConfigGetArgs());if (!RemoteConfigValues.TryGetNumber(config, "reward_multiplier", out var multiplier)) multiplier = 1;RemoteConfigValues.TryGetBool(config, "new_shop_enabled", out var newShop);RemoteConfigValues has TryGetString, TryGetNumber, TryGetBool and
TryGetJson<T>, parsed with the invariant culture and Newtonsoft.Json.
See Feature Flags for a full example.