Flow Context
The Flow Context is the shared runtime object for a single flow execution.
It is how steps communicate with each other without relying on globals, hidden mutable state, or complex parameter threading.
What the context contains
The context gives each step access to:
ctx.input: the original flow inputctx.get(key): read a stored valuectx.set(key, value): store a valuectx.has(key): check if a value exists
If the flow is using cancellation support, the context also carries the execution signal.
Accessing input
The input passed to flow.run(input) is available as ctx.input.
flow.step("validate", (ctx) => {
console.log(ctx.input.userId);
});This input should be treated as the source data for the execution, not as a place to accumulate intermediate state.
Reading and writing data
Use set() and get() to share intermediate data between steps:
flow.step("step-1", async (ctx) => {
ctx.set("orderId", "12345");
});
flow.step("step-2", (ctx) => {
const orderId = ctx.get<string>("orderId");
console.log(orderId);
});Why context matters
Without context, each step would need to recalculate data, re-query dependencies, or rely on out-of-band storage. Context keeps the flow readable and makes compensation practical, because rollback logic often needs IDs produced by earlier steps.
Typical context values include:
- created record IDs
- reservation IDs
- external transaction IDs
- derived values needed later in the flow
Type safety
You can specify types when reading values:
const val = ctx.get<string>("myKey");This keeps larger workflows easier to maintain and reduces accidental misuse of stored values.
The context lifecycle
- Start: created when
run()is called - During Steps: passed to each step function
- During Compensation: passed to compensation functions
- End: discarded when the flow finishes
This is an execution-local object. It does not persist across runs by itself.
Important note
The context is unique to one execution.
That means:
- it is not shared between two separate calls to
run() - it is not a cache
- it is not a replacement for durable storage
Even if two runs use the same idempotency key, they do not share one live in-memory context object.
What to store in context
Good candidates:
- values created by earlier steps that later steps need
- values needed for compensation
- small derived values that improve readability
Poor candidates:
- large binary payloads
- entire database snapshots
- secrets you do not want to risk logging indirectly
- values that should really live in durable storage
Best Practices
- Treat
ctx.inputas read-only. - Use descriptive keys like
paymentIdorreservationId. - Store references, not large objects, when possible.
- Put only execution-relevant data in context.