An application is one TypeScript module built from three pieces:
collection
Named JSON records with string keys.
derive
A synchronous function of records and other derived values. Always private.
query / mutation
Read a snapshot, or make one atomic change. Public only if you list it in http.
A complete example
Save this as examples/counter.ts:
examples/counter.ts
import { collection, define, derive, mutation, query } from "@flower-js/sdk";
const counters = collection<number>("counters");
const doubled = derive("counter.doubled", (ctx, id: string) =>
(ctx.get(counters, id) ?? 0) * 2,
);
const increment = mutation("internal.counter.increment", (ctx, id: string) => {
if (typeof id !== "string" || !id) throw new TypeError("An ID is required");
ctx.set(counters, id, (ctx.get(counters, id) ?? 0) + 1);
ctx.materialize(doubled, id);
return ctx.get(doubled, id); // Includes the write above.
});
const get = query("internal.counter.get", (ctx, id: string) => {
if (typeof id !== "string" || !id) throw new TypeError("An ID is required");
return {
count: ctx.get(counters, id) ?? 0,
doubled: ctx.get(doubled, id),
};
});
export default define({
definitions: [doubled],
http: {
"counter.increment": increment,
"counter.get": get,
},
});Deploy it and call it:
Deploy and call
node sdk/cli.ts deploy examples/counter.ts
node sdk/cli.ts call counter.increment '"visits"' --request-id visit-001
node sdk/cli.ts call counter.get '"visits"'
# value: { count: 1, doubled: 2 }- Only aliases listed in
httpare public. Everything else stays private. - Aliases don't have to match internal names.
- Each deploy replaces the whole list of public methods.
What ctx can do
Queries can read; mutations can also write.
| Read operations | Mutation-only operations |
|---|---|
ctx.get(collection, key) | ctx.set(collection, key, value) |
ctx.get(derived, args) | ctx.delete(collection, key) |
ctx.scan(collection) | ctx.materialize(derived, args) |
ctx.query(collection.by(index).eq(value)) | ctx.unmaterialize(derived, args) |
ctx.range(collection.by(index).range({prefix, lte, limit})) | Bounded pages over an index. |
ctx.now() | Throw to abort the whole mutation. |
getreturnsnullfor a missing record.scanreturns{key, value}rows in key order.- Types aren't checked at runtime. Validate arguments yourself.
Start callbacks faster
If module setup doesn't depend on the call, add --initialization static to build or deploy. Flower initializes the module once and reuses it.
- Globals and closures still reset on every call.
- Memory and execution limits still apply.