Skip to content

Cloudstate Classes

What is Cloudstate?

Cloudstate is a way for you to make JavaScript classes persistent. It’s as simple as adding @cloudstate above your class and giving it a unique id. All properties on the class will be stored automatically. When a method augments a property, it’s changes will be stored once the method completes. If the method crashes during execution, changes to properties will not be persisted.

The static ID tells freestyle that this class is a singleton. You can also use dynamic IDs to create multiple instances of the same class.

@cloudstate
class Counter {
static id = "counter" as const;
count = 0;
increment() {
return ++this.count;
}
}

Cloudstate isn’t limited to just simple properties like numbers. You can also store objects, arrays, Maps, Sets, and many other builtin JavaScript data types. If you wish to store a custom class, you must use the @cloudstate decorator on that class so that freestyle knows to extract it. You can use this functionality to compose more complex behavior.

For example, let’s say we actually want to have many counters. We can give the counter a dynamic ID and create a CounterManager class to manage them.

When you store a cloudstate class on another class, only the properties will be stored. You can update methods even after data has been stored and they will always reflect the latest code.

@cloudstate
class Counter {
id = crypto.randomUUID();
count = 0;
increment() {
return ++this.count;
}
}
@cloudstate
class CounterManager {
static id = "counter-manager";
counters: Counter[] = [];
addCounter() {
const counter = new Counter();
this.counters.push(counter);
return { id: counter.id };
}
getCounters() {
return this.counters.map((counter) => ({
count: counter.count,
id: counter.id,
}));
}
}

How to use cloudstate classes

Now that we have our cloudstate, we can use it in our project. Freestyle provides a very minimal API called useCloud that allows you to reference your cloudstate from your frontend and backend code. In the background, useCloud is using an RPC layer to send requests to the cloudstate runtime and remotely execute methods. This means that all methods will be asynchronous and will return a Promise. You cannot read properties from the class directly, only through methods.

The useCloud function requires 1 argument. An ID to the class you want to use. This could be a static ID or a dynamic ID. You can also pass in a generic type argument to specify the type of class that’s associated with the ID to provide type safety. If your class has a static ID and you’ve annotated it with as const, the id will be inferred by TypeScript.

import { useCloud } from "freestyle-sh";
import type { Counter, CounterManager } from "./counter.ts";
const counterManager = useCloud<typeof CounterManager>("counter-manager");
const counter = await counter.addCounter();

Note that you can import classes as types so ensure that bundlers don’t attempt to bundle your classes into your frontend code.