Restate lets you persist key-value (K/V) state using its embedded K/V store.

Key characteristics

State is only available for Virtual Objects and Workflows. Scope & retention:
  • For Virtual Objects: State is scoped per object key and retained indefinitely. It is persisted and shared across all invocations for that object until explicitly cleared.
  • For Workflows: State is scoped per workflow execution (workflow ID) and retained only for the duration of the workflow’s configured retention time.
Access Rules: You can inspect and edit the K/V state via the UI and the CLI.

List all state keys

To retrieve all keys for which the current Virtual Object has stored state:
const stateKeys = ctx.stateKeys();

Get state value

To read a value by key:
const myString = (await ctx.get<string>("my-string-key")) ?? "my-default";
const myNumber = (await ctx.get<number>("my-number-key")) ?? 0;
Returns null if the key doesn’t exist. See the serialization docs to customize the state serializer.

Set state value

To write or update a value:
ctx.set("my-key", "my-new-value");

Clear state key

To delete a specific key:
ctx.clear("my-key");

Clear all state keys

To remove all stored state for the current Virtual Object:
ctx.clearAll();

Advanced: Eager vs. lazy state loading

Restate supports two modes for loading state in handlers:

Eager state (default)

  • How it works: State is automatically sent with the request when invoking a handler
  • Benefits: State is available immediately when the handler starts executing
  • Behavior: All reads and writes to state are local to the handler execution
  • Best for: Small to medium state objects that are frequently accessed

Lazy state

  • How it works: State is fetched on-demand on get calls from the Restate Server
  • Benefits: Reduces initial request size and memory usage
  • Setup: Enable lazy state in the service or handler configuration
  • Best for: Large state objects that aren’t needed in every handler execution

Advanced: Typed State

You can specify the types of your Object’s K/V state, to ensure type safety and better developer experience:
type GreeterContext = restate.ObjectContext<{
  name: string;
  count: number;
}>;

export const greeter = restate.object({
  name: "Greeter",
  handlers: {
    greet: async (ctx: GreeterContext, name: string) => {
      const count: number = (await ctx.get("count")) ?? 0;
      ctx.set("name", name);
    },
  },
});