Callback contexts
Every callback gets a ctx. Queries and derived values get read methods. Mutations also get write methods.
| API | Contract |
|---|---|
Context | The read-only context, also exported as QueryContext. Valid only during one call; don’t store it. |
ctx.now(): number | Server time in milliseconds, fixed for the call. Keep host clocks in sync: a query’s time can step backward after a leader change. |
ctx.get(collection, key): T | null | Read one record. Null means missing, or that null is stored there. |
ctx.get(derived, args): Value | Compute or reuse a derived value for these arguments. If the derived value failed, its error is thrown here. |
ctx.scan(collection, options?: ScanOptions): { key: string; value: T }[] | Read rows by key or index, with bounds, reverse, offset and limit. Sees the current mutation’s writes. Large offsets still walk the skipped rows; use ctx.range to page. |
ctx.query(query): T[] | Read values matching an equality query (values only, no keys). Sees the current mutation’s writes. |
ctx.range(query): RangePage<T> | Read one ordered page of keys and values. See ranges and cursors. |
MutationContext | Context plus the writes below. Queries can’t write, even with a type cast. |
ctx.set(collection, key, value): void | Replace a record. All of a mutation’s writes commit together. |
ctx.delete(collection, key): void | Remove a record. Deleting a missing key is fine. |
ctx.materialize(derived, args): void | Keep this derived value stored and updated as its inputs change. |
ctx.unmaterialize(derived, args): void | Stop keeping it. Unreachable cached values can be collected; source records stay. |
- A mutation commits all of its writes or none. A throw, an invalid result or a budget overrun discards everything.
- Materialize values you read often. Leave expensive, rarely read values unmaterialized.
- Cycles and very deep derived chains fail.
Query result cache
Flower reuses query results while their inputs are unchanged. Every hit is still authorized per caller and still waits for a fresh read fence. Results that depend on time or managed crypto aren’t cached.
FLOWER_QUERY_CACHE_BYTES (default 16 MiB per logical database) sets the cache size. FLOWER_QUERY_FLIGHT_BYTES (default 512 KiB) bounds sharing between identical in-flight queries. Set either to 0 to disable it.
Definitions and methods
Wrap your functions with derive, query or mutation, then pass them to define. Only aliases in the http table are callable.
| API | Contract |
|---|---|
derive<Args, Value>(name, compute): Derived<Args, Value> | A pure reactive function (ctx, args) => Value. Read it with ctx.get. Not callable over HTTP. |
query<Args, Value>(name, compute, options?): QueryMethod<Args, Value> | A read-only method (ctx: QueryContext, args) => Value. Reads are fresh by default. |
mutation<Args, Value>(name, compute): MutationMethod<Args, Value> | An atomic method (ctx: MutationContext, args) => Value. Validate input and check authorization in your code. |
QueryConsistency | "linearizable" | "replica-local". |
QueryOptions | Optional consistency; default linearizable (fresh). Unknown options are rejected. |
Derived<Args = Json, Value = Json> | Readonly kind: "derived", name, compute, optional aggregate. |
QueryMethod<Args = Json, Value = Json> | Readonly kind: "queryMethod", name, compute, optional consistency. |
MutationMethod<Args = Json, Value = Json> | Readonly kind: "mutationMethod", name, compute. |
Definition | Derived, QueryMethod, MutationMethod or TransactionMethod. |
HttpMethod | QueryMethod, MutationMethod or TransactionMethod (not Derived). |
define(config: ModuleConfig = {}): FlowerModule | Validates and freezes the application. Methods in http and maintenance handlers are registered automatically. Two different definitions with the same name are rejected. |
ModuleConfig | Optional collections, definitions, http (alias → method), maintenance and keys (every managed key your callbacks use). Unknown fields are rejected. |
FlowerModule | The frozen manifest: collections, definitions, http (alias → name, kind and, for queries, consistency), maintenance and keys. |
- A deployment swaps code, aliases, schema and derived state in one step. If it is rejected, the old version keeps running.
- Fresh (linearizable) queries can run on any replica. They confirm with a quorum before reading.
- Replica-local queries can be stale with no bound. They skip the quorum check. Data, code and aliases may lag, and switching replicas can move you back in time. Opt in per query, in code.