NEXSS FLOW · PATTERNS
10 min read | Updated 2026-10-01
Patterns

Nexss Flow Patterns

Real, checked-in .nflow recipes: timeout and fallback, error fallback, parallel actions with deterministic keys, @on_error routing, dynamic dispatch, and scoped filesystem reads.

patternscompositionrecipesexamples

Per-action timeout with local fallback

:timeout sets a deadline for one action; it is not a workflow-wide SLA or a cost cap. If the action returns an error (including a timeout), || tries the local fallback.

nflow nflow
@description "Per-action Timeout and Local Fallback"
@assert: result.recovered == true

{ query: "SELECT * FROM huge_table" }
-> sleep:timeout=1s @{ duration_ms: 5000 }
|| { recovered: true, error: "Database took too long, serving fallback data." }
When to use this
Use when you have a known fallback value for a known slow operation. The fallback is local input, not recovery of an external service. Do not use it to mask a service that should be fixed.

Recover from a failed action with ||

The || operator runs the right side only if the left side fails. The kernel classifies the error; 4xx errors are real errors, not triggers to retry.

nflow nflow
@description "Recover from a failed action with the fallback operator"
@assert: result == "Recovered"

fail @{ kind: "Validation", message: "User email is invalid" }
|| const @{ value: "Recovered" }

Parallel actions with deterministic keys

The & operator fans out branches that run concurrently. Repeated action names get name#1, name#2 in source order — so downstream projections can address each one deterministically.

nflow nflow
@description "Run independent actions concurrently"
@assert: result["const#1"] == "Data A"
@assert: result["const#2"] == "Data B"
@assert: result.noop.request == "local input"

{ request: "local input" }
-> ( const @{ value: "Data A" } & noop & const @{ value: "Data B" } )

The built-in feature suite tests parallel composition with ( const @{ value: "left" } & const @{ value: "right" } ). A name used by only one branch keeps its existing key; repeated names get name#1, name#2. If a generated label is already in use, trailing # characters are added until it is unique.

Scatter-gather with parallel branches

Three concurrent branches whose results are projected into a single object. Duplicate action names receive deterministic keys: sleep#1, sleep#2, sleep#3.

nflow nflow
@description "High-Throughput Parallel Processing (Scatter-Gather)"
@assert: result.a == "Data A"
@assert: result.b == "Data B"
@assert: result.c == "Data C"

(
    sleep @{ duration_ms: 500, value: "Data A" }
  & sleep @{ duration_ms: 500, value: "Data B" }
  & sleep @{ duration_ms: 500, value: "Data C" }
)
-> {
    a: .["sleep#1"].value,
    b: .["sleep#2"].value,
    c: .["sleep#3"].value
}

Error routing with @on_error

Production pipelines break. @on_error routes matching failures to an action. The error.info action returns the recovered error as a structured map.

nflow nflow
@on_error {
  when error.kind == "Timeout"     -> error.info
  when error.kind == "Validation"  -> error.info
  else                              -> error.info
}

@assert: result.kind == "Validation"

fail @{ kind: "Validation", message: "User email is invalid" }

Combine :retry=N on the failing atom with @on_error for layered resilience: the retry handles transient failures, the catch block handles classified failures that escape retry.

Dynamic dispatch with fallback

Dispatch tries configured member actions in order until one succeeds. The first two names are absent; const is the local final candidate.

nflow nflow
@description "Local fallback routing with dispatch"
@assert: result == "I am the fallback!"

{ members: "not_found, broken_service, const", payload: { value: "I am the fallback!" } }
-> dispatch

This exercises fallback routing only, not recovery of an external service. The members list is local input — the dispatch action walks the list, tries each, and returns the first successful result.

Scoped filesystem reads

Walk and read a single checked-in fixture directory. The configured root is a flow argument, not an operating-system sandbox; changing it can expand which files are read.

nflow nflow
@description "Read only the checked-in text fixture from this example's directory"
@assert: len(result) == 1
@assert: result[0].rel_path == "sample.txt"

fs.walk:dir="examples/01_secure_pipeline/fixtures":ext="txt"
-> fs.read
-> collect
Flow argument, not OS sandbox
The root path is a flow argument, not an OS-level sandbox. A different caller can pass a different path and read different files. For untrusted input, combine with the sandbox extension to enforce an OS boundary.

Logging and debug pipelines

Control global logging and debug pipelines effortlessly. Set LOG_LEVEL=debug or LOG_FORMAT=json as environment variables to change the output without editing the flow.

nflow nflow
@description "Control global logging and debug pipelines effortlessly"
@assert: result.status == "done"

{ user_id: 42, action: "login" }

# Emit a structured INFO log (appears in stderr and all observability sinks)
-> log.info @{ message: "User attempting login" }

-> { ..., status: "verified" }

# Dump the current state to the console beautifully without breaking the data pipe
-> debug @{ label: "State After Verification" }

-> { status: "done" }