Guide · 04

Call methods safely.

Call queries and mutations from TypeScript, the CLI or plain HTTP. A request ID makes retries safe.

Call a method

Point the client at any node. Nodes forward writes to the leader for you.

client.ts
import { FlowerClient } from "@flower-js/sdk";

const client = new FlowerClient("http://127.0.0.1:7101");
const result = await client.call("counter.increment", "visits", {
  requestId: "visit-002",
});
// { revision, value, duplicate }

console.log(result);
console.log(await client.call("counter.get", "visits"));
The same mutation over HTTP
curl -fsS http://127.0.0.1:7101/v1/call \
  -H 'Content-Type: application/json' \
  -d '{"name":"counter.increment","args":"visits","requestId":"visit-003"}'
HTTP method routes
EndpointBody
POST /v1/call{name, args?, requestId?, expectedRevision?, credentials?}. Works for any exposed method.
POST /v1/query{name, args?, credentials?}. Queries only.
POST /v1/mutate{name, args?, requestId, expectedRevision?, credentials?}. Mutations and transactions only.
  • All three return {revision, value, duplicate}.
  • Over HTTP, mutations need a requestId. The SDK makes one up if you omit it.

Retry safely

After a timeout, dropped connection, election or 503 UNAVAILABLE, retry with the same request ID and the same arguments, on any node.

  • If the first attempt committed, you get its original result with duplicate: true.
  • A timed-out mutation may still have committed. Never retry it under a new ID.
  • Use a new ID only for a new operation. The CLI makes a new ID unless you pass --request-id.
  • For compare-and-set, pass expectedRevision.

Authorize callers

Pass an authorize query to define. It runs before every call, retry and watch refresh, and returns the caller's identity.

Application authorization
import { define, query, key, jwt } from "@flower-js/sdk";
const sessions = key("sessions", { algorithm: "Ed25519", usages: ["verify"] });
const authorize = query("admission", (_ctx, request) => {
  const { claims } = jwt.verify(request.credentials, sessions);
  if (typeof claims.sub !== "string" || typeof claims.tenant !== "string") return null;
  return { subject: claims.sub, tenant: claims.tenant };
});
export default define({ keys: [sessions], authorize, http: { /* business methods */ } });
  • Return null or throw to deny. Methods read the identity with ctx.principal().
  • Send tokens in credentials, not in arguments, so a refreshed token doesn't break retries.
  • Put checks that depend on changing state in the mutation itself.
  • Protected reads are always fresh, even if the query is replica-local.

More in the authorization reference, including tenant partitions.

Limit how long retry results are kept

Flower keeps results so retries can be answered. To bound that, operators use retry windows (epochs) and retire old ones.

  1. Initialize the history with controlRetention.
  2. Create the client with boundedRetries: true.
  3. Save await client.newRequestId() durably before sending.
  4. Retire an epoch only after the retry window you promised has passed.
  • RETRY_WINDOW_EXPIRED means the mutation may have committed. Don't resend it under a new ID; check your own records.
  • Windows move only when an operator advances them. They aren't TTLs.
Long-running clients, work queues and restores

Long-running clients can use openRetrySession, and call acknowledgeRetrySession only after durably handling results up to that point. Acknowledged sequences never run again.

workQueue claims then carry a history identity; pass it back unchanged. After a restore, old claims are rejected. External systems must check an increasing fencing token too; lease expiry can't stop a paused worker.

See the retention reference for budgets and restores.

Transactions across groups

transaction(name, args => ({ calls: [...] })) runs method calls in several partitions or Raft groups. All commit or none do.

  • Until it resolves, each partition involved blocks fresh reads and writes. Others stay available.
  • Opt-in replica-local reads can still see the older state.

See the transaction guide for setup and recovery.