Call a method
Point the client at any node. Nodes forward writes to the leader for you.
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"));curl -fsS http://127.0.0.1:7101/v1/call \
-H 'Content-Type: application/json' \
-d '{"name":"counter.increment","args":"visits","requestId":"visit-003"}'| Endpoint | Body |
|---|---|
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.
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.
- Initialize the history with
controlRetention. - Create the client with
boundedRetries: true. - Save
await client.newRequestId()durably before sending. - Retire an epoch only after the retry window you promised has passed.
RETRY_WINDOW_EXPIREDmeans 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.