2026-08-29 11:46:20 +03:00
---
name: rudder-web-sdk
2026-09-21 23:39:02 +03:00
description: Use when working with the Rudder TypeScript/JavaScript client SDK (@rudder/sdk ) for web games. Covers createClient, device/google/apple/custom auth, identity linking, state objects (player with balances and items, catalog, storage), module calls through clients generated by `rudder client generate` , idempotent retries, and error handling. Load this whenever code imports from @rudder/sdk or a generated rudder.modules.ts.
2026-08-29 11:46:20 +03:00
---
2026-09-17 15:28:08 +03:00
# Rudder Web SDK (@rudder/sdk)
2026-08-29 11:46:20 +03:00
2026-09-17 15:28:08 +03:00
Client SDK for web games on the Rudder LiveOps platform. Source of truth:
`liveops-web-sdk/src` (wire types in `src/generated` come from
`liveops-backend/apigen` ; never edit them by hand).
2026-08-29 11:46:20 +03:00
2026-09-24 14:24:45 +03:00
0.2.0 covers only the kernel: auth, player, catalog, storage, module calls and
2026-09-17 15:28:08 +03:00
internal revision sync. Game features are installed modules, reached through a
typed client generated from their manifests. There is no `resources(slug)` and
no `projectStorage` ; game-facing configuration comes from modules such as
`remote_config` .
2026-09-17 12:08:03 +03:00
2026-08-29 11:46:20 +03:00
## Install
2026-09-17 15:28:08 +03:00
Published to the Gitea registry at hub.rudder.build, not npmjs. Point the
`@rudder` scope at it in `.npmrc` (anonymous read, no token):
2026-08-29 11:46:20 +03:00
```
@rudder:registry=https://hub.rudder.build/api/packages/rudder/npm/
```
``` bash
2026-09-17 15:28:08 +03:00
npm install @rudder/sdk
2026-08-29 11:46:20 +03:00
```
ESM + CJS + `.d.ts` ; `type: module` ; zero runtime dependencies.
## Init
``` ts
2026-09-17 15:28:08 +03:00
import { createClient } from '@rudder/sdk' ;
2026-08-29 11:46:20 +03:00
2026-09-17 15:28:08 +03:00
const client = createClient ( {
projectKey : 'your-project-key' , // required; selects staging or prod
// baseUrl?: string — default 'https://api.rudder.build'
2026-08-29 11:46:20 +03:00
// tokenStore?: TokenStore — default: localStorage, in-memory fallback
2026-09-17 15:28:08 +03:00
// appVersion?: string — sent with platform 'web' on login/refresh
2026-08-29 11:46:20 +03:00
// requestTimeoutMs?: number — default 10000
// syncIntervalMs?: number — revision poll, default 30000 (±20% jitter)
} ) ;
```
2026-09-17 15:28:08 +03:00
A missing `projectKey` throws `RudderError` with
`code: 'sdk/invalid-options'` . Call `client.dispose()` on unmount/HMR to stop
the sync poll and drop cached state.
2026-08-29 11:46:20 +03:00
## Auth essentials
``` ts
await client . auth . loginWithDevice ( { region , language , nickname } ) ; // device id auto-generated/persisted
2026-09-08 16:05:00 +03:00
await client . auth . loginWithGoogle ( { idToken , region , language , nickname } ) ;
await client . auth . loginWithApple ( { idToken , region , language , nickname } ) ;
await client . auth . loginWithCustom ( { customData , provider ? , region , language , nickname } ) ;
await client . auth . linkWithGoogle ( idToken ) ;
await client . auth . linkWithApple ( idToken ) ;
await client . auth . linkWithCustom ( { customData , provider ? } ) ;
await client . auth . unlinkIdentity ( provider ) ; // 'device' | 'google' | 'apple' | 'custom' | 'custom:<name>'
2026-08-29 11:46:20 +03:00
client . auth . logout ( ) ;
2026-09-17 12:08:03 +03:00
client . auth . isAuthenticated ;
client . auth . onAuthStateChange ( cb ) ;
2026-08-29 11:46:20 +03:00
```
Tokens are saved to the `TokenStore` automatically. The transport injects
2026-09-17 15:28:08 +03:00
`Authorization: Bearer <token>` and single-flight refreshes on 401; a failed
refresh clears tokens and emits `signed-out` . Details:
2026-08-29 11:46:20 +03:00
[reference/auth.md ](reference/auth.md ).
2026-09-17 12:08:03 +03:00
## Client surface
2026-08-29 11:46:20 +03:00
| Area | Access | Kind |
|---|---|---|
| Auth | `client.auth` | service |
2026-09-21 23:39:02 +03:00
| Player | `client.player` (`player` , `identities` , `balances` , `items` ) | `SyncedState<PlayerProfile>` |
2026-09-17 15:28:08 +03:00
| Catalog currencies | `client.catalog` | `SyncedState<Map<string, CatalogCurrency>>` |
| Player storage | `client.storage` + `.save(type, data)` / `.delete(type)` | `SyncedState<GetStorageResponse>` + mutations |
| Modules | `client.modules.call<T>(module, fn, args?, { moduleVersion? })` | resolves to `result` as `T` |
## Module calls through generated clients
``` bash
RUDDER_URL = ... RUDDER_TOKEN = ... RUDDER_PROJECT = ... \
rudder client generate --lang ts --out src/rudder.modules.ts [ --environment staging| prod]
```
``` ts
import { modules } from './rudder.modules' ;
const m = modules ( client ) ;
await m . leaderboards . submit ( { slug : 'weekly' , score : 120 } ) ;
const top = await m . leaderboards . top ( { slug : 'weekly' , limit : 10 } ) ;
```
The generated file is a factory over `client.modules.call` , with argument and
result types, and a `<Module><Fn>Error` code union per function. Regenerate
and commit it when installed modules change. Details:
[reference/modules.md ](reference/modules.md ).
2026-08-29 11:46:20 +03:00
2026-09-17 15:28:08 +03:00
## State objects: the shared pattern
`player` , `catalog` and `storage` extend `SyncedState<T>` :
- `.value: T | undefined` , `.status: 'idle' | 'loading' | 'ready' | 'error'` ,
`.error?: Error`
- `onChange(cb: (snapshot: SyncedSnapshot<T>) => void): () => void` : fires
immediately with `{ status, value, error }` ; maps onto React
`useSyncExternalStore`
- `load(): Promise<T>` : deduplicated, no-op when ready
- `reload(): Promise<T>` : forced refetch
`player` and `catalog` are warmed at login; `storage` loads on first use. A
revision poll (`GET /sdk/v1/sync` , keys `profile` , `catalog` , `storage` ) runs
every 30 s (±20% jitter), pauses while the tab is hidden, and reloads the
states whose revision grew. Storage mutations invalidate immediately. Call
2026-09-21 23:39:02 +03:00
`player.reload()` after a module call that changes balances or items when
2026-09-17 15:28:08 +03:00
you need fresh values right away.
2026-08-29 11:46:20 +03:00
## Errors
All SDK errors extend `RudderError` (optional machine-readable `code` ):
2026-09-17 15:28:08 +03:00
- `RudderNetworkError` : fetch failed or timed out (`error.cause` holds the original)
- `RudderHttpError` : non-2xx, carries `status` , `statusText` , `body` , `code`
- `RudderAuthError extends RudderHttpError` : 401 after a failed refresh;
tokens already cleared, `signed-out` already emitted
- `RudderModuleError<C> extends RudderHttpError` : a module call returned 422
`{ error, code, requestId }` ; exposes `code: C` , `message` , `requestId`
2026-08-29 11:46:20 +03:00
- `RudderError` with `code: 'sdk/invalid-options'` (`SDK_ERROR_INVALID_OPTIONS` )
2026-09-17 12:27:52 +03:00
2026-09-17 15:28:08 +03:00
Compare `error.code` with string literals. A module trap is a 500 and a
timeout a 504, both without details.
2026-08-29 11:46:20 +03:00
## Reference files
2026-09-17 15:28:08 +03:00
- [reference/auth.md ](reference/auth.md ): login flows, options, token store, session lifecycle
2026-09-21 23:39:02 +03:00
- [reference/player.md ](reference/player.md ): profile, balances, items
2026-09-17 15:28:08 +03:00
- [reference/storage.md ](reference/storage.md ): player storage, limits
- [reference/modules.md ](reference/modules.md ): generated clients, `modules.call` , idempotency, retries, errors