Skip to main content

SDK Server Reference

Complete reference for @zaflun/lumio-sdk/server — the server-side library used in server/functions.ts.

Import

import { action, query, v } from "@zaflun/lumio-sdk/server";

Defining functions

action

Defines a server function that can be called by the client via useLumioAction() or useMutation(). Actions have full access to storage, secrets, egress, and real-time push.

function action<TArgs, TReturn>(definition: {
args: ArgSchema<TArgs>;
handler: (ctx: ActionContext, args: TArgs) => Promise<TReturn>;
}): Action<TArgs, TReturn>;
export const incrementScore = action({
args: { amount: v.number() },
handler: async (ctx, args) => {
const current = (await ctx.db.get<number>("install", "score")) ?? 0;
const next = current + args.amount;
await ctx.db.set("install", "score", next);
return { score: next };
},
});

query

Defines a read-only server function. Queries cannot write to storage, push real-time events, or call external APIs. They are intended for fetching state and may be cached by the platform.

function query<TArgs, TReturn>(definition: {
args: ArgSchema<TArgs>;
handler: (ctx: QueryContext, args: TArgs) => Promise<TReturn>;
}): Query<TArgs, TReturn>;
export const getScore = query({
args: { userId: v.string() },
handler: async (ctx, args) => {
return ctx.db.get<number>("user", args.userId, "score") ?? 0;
},
});

ActionContext

The ctx parameter in action() handlers.

ctx.db — Storage

Reads and writes extension storage, namespaced by scope.

// Read a value
const value = await ctx.db.get<T>(scope, ...keys);

// Write a value
await ctx.db.set(scope, ...keys, value);

// Delete a value
await ctx.db.delete(scope, ...keys);

// List all keys in a scope (with optional prefix)
const keys = await ctx.db.list(scope, prefix?);

Scopes:

ScopeIsolationCapacity
"global"Shared across all installs100 MB
"install"Per overlay install10 MB
"user"Per authenticated viewer (interactive page only)1 MB

ctx.fetch — External HTTP

Calls an external HTTPS endpoint. Subject to the egress.allowHosts allowlist declared in lumio.config.json.

const res = await ctx.fetch("https://api.example.com/data");
const json = await res.json();

Has the same interface as the standard fetch() API. Restrictions:

  • HTTPS only
  • No private IP ranges or localhost
  • Maximum 10 calls per action invocation
  • 30-second timeout per request

ctx.secrets — Secret access

Reads secrets stored via lumio secrets set or the dashboard.

const apiKey = ctx.secrets.get("MY_API_KEY"); // throws if not set
const exists = ctx.secrets.has("MY_API_KEY"); // boolean check

Secret values are never logged or returned to the client.


ctx.realtime — Push real-time events

Pushes an event to all connected surfaces (layer, editor, interactive page) for this install.

await ctx.realtime.push("score:updated", { score: 42 });

Clients receive the event via a WebSocket subscription. Listen with:

window.addEventListener("lumio:realtime", (event) => {
if (event.type === "score:updated") {
console.log(event.data.score);
}
});

Maximum 64 KB payload. Maximum 100 pushes/second per extension.


ctx.cache — Scoped cache

Scoped Redis key-value cache. Faster than ctx.db for temporary data that can be lost.

await ctx.cache.set("counter", 42, 3600); // set with TTL in seconds
const val = await ctx.cache.get("counter"); // get value
await ctx.cache.increment("counter", 1); // atomic increment
await ctx.cache.delete("counter"); // delete key

Keys are scoped per install (ext-cache:\{install_id\}:*). TTL range 1s–24h, default 1h. See Cache & Background Jobs.


ctx.defer — Background work

Queue a function to run after the handler returns — useful for background DB writes after a fast cache read.

ctx.defer(async () => {
await ctx.db.set("install", "last_run", Date.now());
});

Max 5 deferred calls per handler invocation. Each has a 10s timeout. See Cache & Background Jobs.


ctx.identity — Caller identity

Present when the action is called from the interactive page by an authenticated viewer. null on the layer and editor surfaces.

if (ctx.identity) {
console.log(ctx.identity.userId);
console.log(ctx.identity.platform); // "twitch" | "youtube" | ...
console.log(ctx.identity.platformUserId);
}

QueryContext

The ctx parameter in query() handlers. A subset of ActionContext — read-only access only.

PropertyAvailable
ctx.db.getYes
ctx.db.listYes
ctx.db.setNo — throws
ctx.fetchNo — throws
ctx.secretsYes (read-only)
ctx.realtimeNo — throws
ctx.identityYes

Validation — v

The v object provides runtime argument validators. All args must be declared with v — undeclared fields are stripped before the handler runs.

// Primitives
v.string()
v.number()
v.boolean()
v.null_()

// Modifiers
v.optional(v.string()) // string | undefined — field may be absent
v.nullable(v.string()) // string | null

// Collections
v.array(v.string())
v.object({ name: v.string(), count: v.number() })

// Union
v.union([v.string(), v.number()])

// Literal
v.literal("active")

Validation happens before the handler is called. Invalid args return a structured error to the caller without invoking the handler.


Exports from server/functions.ts

Every named export from server/functions.ts that is an action() or query() result is automatically registered with the platform at deploy time. No additional registration step is needed.

// server/functions.ts
export const getScore = query({ ... }); // registered as "getScore"
export const setScore = action({ ... }); // registered as "setScore"

// Not registered — not an action or query
export const MULTIPLIER = 2;

The exported name becomes the actionName / queryName used on the client.


Bot module handlers

Bot module extensions (category: "bot_module") use a separate set of handler wrappers for chat interaction. Each wrapper sets a __type discriminant on the exported function, following the same pattern as query() and action().

Handler type reference

Wrapper__type__triggerSync/AsyncTimeout
command(name, handler)"command"Command nameSync500ms
keyword(word, handler)"keyword"Keyword stringAsync10s
pattern(regex, handler)"pattern"Regex stringAsync10s
event(type, handler)"event"Event typeAsync10s
timer(name, handler)"timer"Timer nameAsync10s
moderate(handler)"moderate"N/ASync500ms

Signatures

function command(
name: string,
handler: (ctx: BotModuleContext, args: string[]) => Promise<HandlerResponse | null>
): CommandDefinition;

function keyword(
word: string,
handler: (ctx: BotModuleContext, message: ChatMessage) => Promise<HandlerResponse | null>
): KeywordDefinition;

function pattern(
regex: string,
handler: (ctx: BotModuleContext, message: ChatMessage, matched: string) => Promise<HandlerResponse | null>
): PatternDefinition;

function event(
eventType: string,
handler: (ctx: BotModuleContext, evt: Record<string, unknown>) => Promise<HandlerResponse | null>
): EventDefinition;

function timer(
name: string,
handler: (ctx: BotModuleContext) => Promise<HandlerResponse | null>
): TimerDefinition;

function moderate(
handler: (ctx: BotModuleContext, message: ChatMessage) => Promise<ModerationResponse>
): ModerateDefinition;

BotModuleContext

See Bot Module Context for the full API reference.

HandlerResponse

interface HandlerResponse {
reply?: string;
actions?: ActionRequest[];
}

interface ActionRequest {
action: string;
params: Record<string, unknown>;
}

ModerationResponse

interface ModerationResponse {
block: boolean;
action?: "delete" | "timeout" | "ban";
duration?: number;
reason?: string;
}

ChatMessage

interface ChatMessage {
text: string;
platformMessageId: string;
}