Skip to main content
This example builds a full-stack todo app where the browser connects to the iii engine over a single WebSocket. That one connection handles function invocations (create, toggle, delete, list) and delivers real-time stream change events back to the UI — no polling, no separate event channel.

Engine configuration

The iii-config.yaml defines the engine workers:
iii-config.yaml
The RBAC configuration on port 3111 references todo-project::auth-function and explicitly lists the five functions the browser is allowed to call. See Worker RBAC for the full reference.

Backend

Worker setup

The API worker connects to the engine’s internal port and exports a shared logger:
src/iii.ts

Auth function (RBAC)

The auth function runs on every new WebSocket connection to the RBAC port. It creates a session ID and uses function_registration_prefix so each browser session gets its own namespace — the engine automatically prefixes function IDs registered by that session, preventing collisions.
src/lib/rbac.ts

Stream wrapper

The TodoStream class provides a typed interface over the engine’s built-in stream operations. Each method triggers the corresponding stream::* function:
src/routes/todos.stream.ts

Functions

All functions are registered with fn(), a thin wrapper around iii.registerFunction. There are no HTTP routes — the browser calls these functions directly over the WebSocket connection.
src/routes/todos.create.ts

Frontend

Connection

The browser connects to the RBAC port using iii-browser-sdk. This single connection is used for both triggering functions and receiving real-time stream updates:
src/lib/iii.ts

Real-time hook

The useTodos hook manages the full lifecycle — initial fetch, live updates, and CRUD operations — all through the same WebSocket connection:
src/hooks/use-todos.ts
The hook does three things on mount:
  1. Fetches the current list by triggering todos::list.
  2. Subscribes to real-time changes by registering a local function (ui::on-todo-change) and binding it to a stream trigger on the todo stream. The engine pushes StreamChangeEvent payloads (create, update, delete) whenever the stream changes.
  3. Cleans up by unregistering both the function and the trigger on unmount.
All CRUD operations (addTodo, toggleTodo, deleteTodo) call iii.trigger with the corresponding function ID. The response arrives over the same connection, and the stream trigger delivers the update to all connected clients.

Key concepts

  • Single connection — The browser opens one WebSocket to the RBAC port. Function calls and real-time stream events flow over the same connection.
  • No HTTP routes — The API worker registers plain iii functions. The browser invokes them directly via iii.trigger. There is no REST layer.
  • RBAC — The engine’s iii-worker-manager supports auth functions, expose lists, and middleware. This example uses a simple auth function that creates a session. See Worker RBAC for the full reference.
  • Engine-managed streams — The iii-stream worker handles persistence (file-based KvStore in this example). The API worker reads and writes through stream::* function triggers — no custom stream implementation required.
  • Session isolation — The auth function returns a function_registration_prefix. The engine prefixes every function registered by that browser session, so multiple clients can register ui::on-todo-change without colliding.