Reference · 03

Contexts and definitions.

What a method can do with ctx, and how define builds the public method list.

Callback contexts

Every callback gets a ctx. Queries and derived values get read methods. Mutations also get write methods.

APIContract
ContextThe read-only context, also exported as QueryContext. Valid only during one call; don’t store it.
ctx.now(): numberServer 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 | nullRead one record. Null means missing, or that null is stored there.
ctx.get(derived, args): ValueCompute 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.
MutationContextContext plus the writes below. Queries can’t write, even with a type cast.
ctx.set(collection, key, value): voidReplace a record. All of a mutation’s writes commit together.
ctx.delete(collection, key): voidRemove a record. Deleting a missing key is fine.
ctx.materialize(derived, args): voidKeep this derived value stored and updated as its inputs change.
ctx.unmaterialize(derived, args): voidStop 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.

APIContract
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".
QueryOptionsOptional 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.
DefinitionDerived, QueryMethod, MutationMethod or TransactionMethod.
HttpMethodQueryMethod, MutationMethod or TransactionMethod (not Derived).
define(config: ModuleConfig = {}): FlowerModuleValidates and freezes the application. Methods in http and maintenance handlers are registered automatically. Two different definitions with the same name are rejected.
ModuleConfigOptional collections, definitions, http (alias → method), maintenance and keys (every managed key your callbacks use). Unknown fields are rejected.
FlowerModuleThe 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.