Skip to content

Logic Nodes

Logic nodes run inline code directly inside Requence. They are useful for:

  • Transforming data between service calls
  • Making routing decisions
  • Filtering or aggregating results

Logic nodes can be written in TypeScript, JavaScript, or Python — all three are full-featured and run the complete context API described below. The default language is TypeScript.

Requence infers types for every language: TypeScript and JavaScript through the TypeScript compiler, and Python through a Python type checker. The inferred types propagate along the graph and validate connections, so your task template stays type-safe regardless of the language you choose.

Everything is available through the context variable:

  • context.input — the input data arriving at this node, fully typed based on upstream nodes
  • context.getNodeData(alias) — retrieve the output of a previous node by its alias
  • context.getNodeError(alias) — retrieve the error message of a failed node by its alias, or null if it succeeded
  • context.store.private(name, binding?) / context.store.shared(name, binding?) — a handle to one remembered value. The verb says who can read it (this node only, or every node here); the argument says how long it lives ('task' for the rest of this task, 'taskTemplate' across runs of this template) and defaults to 'task'. Useful in loops and continuous tasks. See The Store
  • context.toOutput(name, value?) — route execution to a specific named output
  • context.overwrite(data) — replaces the entire task data. Useful when there are intermediate values that don’t need to be in the final result.
  • context.variables.get('key') — read a variable from the template’s scope. The key must be a literal string, there is no await, and the return type is the shape of the stored value. See Referencing a variable in a script.

Logic nodes have an implicit void output — if the node doesn’t return anything, execution continues without modifying the task data. There is no need to explicitly return an empty object.

In addition to context, the following globals are available:

  • fetch — for making HTTP requests
  • console — for logging (log, warn, error)
  • crypto — Web Crypto API
  • setTimeout / setInterval — timers
  • ReadableStream / WritableStream / TransformStream — Web Streams API
  • RequenceFile / RequenceStream — see File Handling
  • createCallback — see Callbacks. A logic node’s own callback is always connection-bound; only a service can declare a durable one.

All schemas used in the task template are automatically available as types inside logic nodes. This includes schemas defined on the entry node, exit nodes, any service version referenced by a service node, and any sub task node — as well as all schemas that those schemas reference, no matter how deeply nested. You do not need to import or configure anything; Requence resolves the full dependency tree and provides the types automatically.

Python logic nodes run the same context API, but follow Python naming and value conventions:

TypeScript / JavaScript Python
context.input (object) context.input (dict)
context.getNodeData(alias) context.get_node_data(alias)
context.getNodeError(alias) context.get_node_error(alias)
context.store.private<T>(name, binding?) / .shared<T>(…) context.store.private(name, binding?) / .shared(…) — the value type is a variable annotation: cursor: StoreSlot[int] = …
context.toOutput(name, value?) context.to_output(name, value)
context.overwrite(data) context.overwrite(data)
context.variables.get('key') context.variables.get('key') — same name, not snake_cased

The most common gotcha: context.input is a dict, so read fields with subscript access, not attribute access.

# ✅ correct
value = context.input["value"]
# ❌ wrong — dicts have no attribute access
value = context.input.value

The results of context.get_node_data(alias) are dicts too, so the same subscript rule applies to node data.

Store calls are awaited in a Python logic node, exactly as in a TypeScript one, and the value type is pinned by annotating the variable that holds the handle:

cursor: StoreSlot[int] = context.store.private('lastUid', 'taskTemplate')
await cursor.set((await cursor.get() or 0) + 1)

The RequenceFile, RequenceStream, and create_callback globals are available in Python. The JavaScript-only globals listed above (fetch, console, crypto, timers, Web Streams) are not injected — use Python’s standard library instead (e.g. urllib/requests for HTTP, print for logging, hashlib for crypto).

  • Max execution time — maximum time in milliseconds the node is allowed to run. Defaults to 1000 ms. When exceeded, the node triggers its on fail output.
  • Concurrency — whether multiple instances of the node can run in parallel. Defaults to on.

When concurrency is enabled, every activation of the logic node spawns a new computation context immediately. When concurrency is turned off, a new context is only spawned after the previous invocation completes. This is useful in continuous task templates and loops — and it is the only thing that stops two activations of the same node overwriting each other in the store, which does not arbitrate concurrent writes.