Controle de fluxo

branch/switch/foreach/parallel/try/group compõem lógica condicional e iterativa sem sair do YAML.

when: o caminho barato

Todo step aceita três campos universais:

Campo Efeito
type Obrigatório. A chave que resolve a implementação
id Opcional. Nomeia o step. Só publica em steps.<id>.result nos steps que produzem resultado — ver abaixo
when Opcional. Expressão booleana; o step só executa se verdadeira

id: não é um mecanismo universal de captura. Declará-lo num step que não produz resultado (map, set, log, publish, validate, fail, return, db.persist/update/delete/sql, cache.set, cache.invalidate, storage.delete) é aceito no build e simplesmente não publica nada — nenhum erro avisa. Publicam em steps.<id>.result: db.find, db.exists, db.query, cassandra.query, dynamo.query, cache.get, http.call, grpc.call e os storage.* de leitura e escrita. A forma que cada um publica está na referência de steps.

when é mais barato que branch para o caso comum de pular um único step:

- { type: db.find, id: found, entity: widget, where: { id: "input.route.id" } }
- { type: fail, when: "steps.found.result = null", status: 404, code: WIDGET_NOT_FOUND }
- type: return

Use branch/switch quando o que muda é qual grupo de steps roda, não se um único step roda.

Os steps de controle de fluxo

Step Função
branch when (campo do próprio step, seleciona entre os dois ramos) / then / else
switch cases: [{ when, then }], mais default
foreach in (expressão), steps, parallelism opcional (default 1), collect: { to, select }
parallel branches: [[...], [...]] — sub-pipelines independentes, executados concorrentemente
try try / catch opcional / finally opcional — a falha fica disponível em error dentro de catch
group Agrupa steps só para legibilidade/organização, sem when próprio nem outro efeito

branch é o único step com when como campo do próprio bloco — ele escolhe entre then e else. Todo outro step usa o when universal descrito acima.

- type: switch
  cases:
    - { when: "input.body.type = 'premium'", then: [ { type: map, to: vars.discount, expr: "0.2" } ] }
    - { when: "input.body.type = 'standard'", then: [ { type: map, to: vars.discount, expr: "0.1" } ] }
  default: [ { type: map, to: vars.discount, expr: "0" } ]

- type: foreach
  in: "input.body.items"
  steps:
    - { type: map, to: vars.line, expr: "{ 'sku': item.sku, 'total': item.price * item.quantity }" }
  collect:
    to: vars.lines
    select: vars.line

- type: try
  try:
    - { type: http.call, integration: pricing, method: POST, path: /quote }
  catch:
    - { type: log, message: "falha ao consultar preço, seguindo com o fallback" }
    - { type: map, to: vars.price, expr: "0" }

Dentro de um foreach, o elemento da iteração é item e a posição é index — campos comuns do estado, não variáveis JSONata. Escrever $item (com cifrão) é o erro mais fácil de cometer aqui, e ele falha em silêncio: $item é uma variável JSONata não definida, avalia para vazio, e o foreach termina sem erro produzindo resultados vazios. item/index também estão disponíveis na expressão de collect.select.

No log, message e os valores de fields são literais — não são avaliados como expressão. message: "'erro: ' & error.message" não interpola nada: registra o texto JSONata cru, exatamente como escrito.

Escopo de id e de vars

Cada bloco composto (then/else/cases[].then/default/foreach.steps/parallel.branches/ try/catch/finally) tem seu próprio escopo de ids — uma referência steps.<id> de fora desse bloco falha o build, não silenciosamente em runtime.

vars, ao contrário, não é escopado por bloco: uma escrita feita dentro de um foreach/try/etc. permanece visível depois dele.

Um detalhe que morde: transação e concorrência

Não misture db.* concorrente com transaction: required. Um parallel com mais de um ramo fazendo db.*, ou um foreach com parallelism > 1 chamando db.*, dentro de uma rota transacional, colide — a sessão transacional é uma única conexão, não é seguro usá-la concorrentemente.

Duas formas seguras de evitar isso:

  • Pareie no máximo um ramo de db.* com computação pura dentro de parallel (o outro ramo não toca o banco).
  • Rode o foreach sequencial (parallelism: 1, que já é o valor padrão) sempre que ele faz db.* dentro de uma rota transacional.

Para onde ir daqui