Writing Packages
Introduction
Freestyle enables a new kind of opensource package: Full Stack Feature Packages. Full Stack Feature Packages come with a whole feature: frontend, backend, and cloudstate, all in one package. When a user installs them, the backend can be deployed in their backend, and the components can be used in their frontend. This removes the need for third-party services, allows for better customization, and empowers opensource developers to share features as packages.
Writing a Package
Prerequisites
Setting up a new package
-
Create a new package
Terminal window npm init -y -
Install Freestyle:
Terminal window npm install freestyle-sh
Writing the Package
To show you how to write a package, we will write a simple counter package. The package will have a counter that can be incremented and decremented.
-
Create a new file
src/index.ts
:import { cloudstate, invalidate } from "freestyle-sh";@cloudstateexport class SimpleCounter {static id = "simple-counter";count = 0;increment() {this.count++;invalidate(useCloud("simple-counter").getCount);}decrement() {this.count--;invalidate(useCloud("simple-counter").getCount);}getCount() {return this.count;}} -
Create the frontend in
src/Counter.tsx
:import { useCloud } from "freestyle-sh";import { useCloudQuery } from "freestyle-sh/react";export function Counter() {const counter = useCloud("simple-counter");let { data: count } = useCloudQuery(counter.getCount);return (<div><button onClick={counter.increment}>Increment</button><button onClick={counter.decrement}>Decrement</button><div>{count}</div></div>);} -
Configure
package.json
:Fullstack Feature Packages can support multiple frontend frameworks from the same package, we recommend you configure
exports
in yourpackage.json
.{"exports": {".": {"node": "./src/index.ts","default": "./src/index.ts"},"./react": {"node": "./src/Counter.tsx","default": "./src/Counter.tsx"}}}This pattern allows your single package to support multiple frontend frameworks and to work with ones you haven’t built yet.
-
Publish your package:
Terminal window npm publish
Advantages
-
Extensibility: Users can extend your package by adding new features or modifying existing ones.
For example, a user could add a
reset
function to theSimpleCounter
class:@cloudstateclass MyCounter extends SimpleCounter {static id = "my-counter";reset() {this.count = 0;invalidate(useCloud("simple-counter").getCount);}} -
Composability: Database packages for other databases never took off, because they were too hard to make work with your other code. Your packages automatically fit onto the user’s backend, frontend, and other packages.
@cloudstateclass MyCounter extends SimpleCounter {id = crypto.randomUUID(); // override so its not a singleton}@cloudstateclass CounterManager {static readonly id = "counter-manager";counters: Record<string, MyCounter> = {};createCounter(id: string) {this.counters[id] = new SimpleCounter();}getCounter(id: string) {return this.counters[id];}} -
Integration: Many existing opensource providers provide packages that need to be hosted separately from users’ code. Your packages can be installed and run in the user’s codebase.