Primeiro workflow: acionamento manual → editar campos → código

A questão das cotações eu calculei à mão em planilhas por dois anos: quantidade vezes preço unitário, mais frete, depois dividir de volta para calcular o custo unitário. Duzentas sacolas, preço unitário um e oitenta e cinco, frete sessenta, o conjunto todo sai por quatrocentos e trinta, unitário dois e quinze. Os números não são complicados, o problema é que eu esqueci o frete duas vezes, e a cotação já tinha sido enviada ao cliente.

Depois esse cálculo foi para o n8n, três nós, muda um número e executa de novo, dois segundos para gerar o novo preço. Este artigo cobre as três etapas do canvas em branco até funcionar, incluindo a armadilha de quando calculei 430 como 37060 pela primeira vez.

Pré-requisitos são só dois: n8n já instalado e abrindo (se não instalou, veja《Como instalar o n8n》), e familiaridade com canvas, painel de nós e histórico de execução (se não estiver familiarizado, consulte《Guia da interface》). Não precisa saber programar, basta copiar o bloco de código inteiro.

Três nós, cada um cuida de uma coisa

O erro mais comum dos iniciantes é tratar os três nós como "três etapas". Eles não são etapas, são três papéis.

  • Manual Trigger (acionamento manual): cria um dado vazio e envia adiante. Ele não consulta banco de dados, não recebe requisições, não olha o horário, é puramente "você clica uma vez e ele roda uma vez". Usá-lo no primeiro workflow serve para reduzir as variáveis a uma — a questão de onde vêm os dados não precisa ser preocupação por enquanto.
  • Edit Fields (editar campos): insere campos no item, ou altera os existentes. A quantidade não muda, entra um e sai um. Em tutoriais antigos ele se chama Set, o nome mudou faz um tempo, o painel de nós do 2.43.7 mostra Edit Fields (Set), os dois nomes são pesquisáveis.
  • Code (código): o único passo que pode alterar a quantidade. Um pode ser dividido em vários, vários podem ser comprimidos em um. Por isso seu valor de retorno deve ser um array — essa é a maior diferença em relação aos outros nós, e também onde mais ocorrem erros.

Minha instância é a 2.43.7, com a interface em chinês simplificado. Um aviso: a interface em chinês só traduziu os textos descritivos e botões, nomes de nós e nomes de parâmetros continuam em inglês. Se você pesquisar "编辑字段" não encontra nada, precisa pesquisar edit fields. Aqueles tutoriais na internet que escrevem "nó Set" "Keep Only Set Fields do nó Set", também não batem com a interface 2.x, não duvide de si mesmo.

O que exatamente é transmitido entre os nós

Sem entender isso, cada erro depois só pode ser adivinhado.

No n8n, todos os dados que fluem entre os nós são um array, e cada elemento do array se parece com isso:

[
  { "json": { "qty": 200 } },
  { "json": { "qty": 150 } }
]

Os colchetes externos são "um lote", dentro dele cada objeto é um dado, o n8n chama isso de item. Os campos reais de negócio estão inseridos em json esta chave. Portanto, "processar 200 pedidos" neste modelo não é fazer um loop 200 vezes, mas sim entregar 200 items ao nó de uma só vez, e o nó os processa um a um.

Este design traz duas consequências. A boa notícia é que os nós naturalmente trabalham em lote, você não precisa escrever loops. A má notícia é que quando você escreve código, o que está em mãos é um array, não um objeto individual——row.qty Esta forma de escrita não pode ser usada diretamente em um array, é preciso primeiro extrair o primeiro elemento.

Do canvas vazio aos três nós

  1. No canto superior direito da lista de workflows, "Novo workflow", ou digite diretamente na barra de endereços /workflow/new. Eu prefiro o segundo, é mais rápido.
  2. No canvas vazio, clique no canto superior direito naquele +(também pode clicar no sinal de mais no centro do canvas), o painel exibe "O que acontece a seguir?".
  3. Na caixa de busca, digite manual trigger, selecione Manual Trigger.
  4. Mova o mouse sobre o pequeno círculo à direita do nó, aparece um +, clique nele, busque edit fields, selecione Edit Fields (Set). Adicione desta forma, as conexões se conectam sozinhas, sem precisar arrastar manualmente.
  5. Repita mais uma vez, pesquise code, selecione Code.
  6. O nome do nó é em inglês por padrão. Dê um duplo clique no título para alterar, eu mudei para chinês: 手动触发、填写报价单、计算金额. Quando há muitos nós, nomes em inglês como Trigger, Set, Code são todos iguais ao passar os olhos, mudar para chinês é puramente para poupar a vista, sem nenhum impacto funcional.
n8n 添加节点面板里搜索 edit fields,结果为 Edit Fields (Set) 节点
Pesquise edit fields e aparece apenas um resultado. A linha pequena abaixo Modify, add, or remove item fields deixa clara sua função: apenas modifica campos, não cria dados.

Editar campos: adicionar quatro campos a um registro de dados

Dê um duplo clique em 「填写报价单」 para abrir o painel, apenas dois parâmetros precisam ser alterados.

Mode Mantenha Manual Mapping, que é o método de preenchimento de formulário. A outra opção JSON Output é escrever diretamente um trecho de JSON, mais prático para estruturas aninhadas, não use por enquanto na fase inicial.

Fields to Set adicione quatro linhas. Em cada linha, à esquerda está o nome do campo, no meio o dropdown de tipo, à direita o valor:

Nome do campo Tipo 值
product string saco de compressão a vácuo 60x80cm
qty número 200
unit_price número 1.85
freight número 60

O dropdown de tipo vem por padrão como "string"; qty, unit_price e freight precisam ser alterados manualmente para "número". A interface não vai te impedir, e se você escolher errado ela também não dá erro, silenciosamente salva 200 como "200". Quão doloroso é essa armadilha, vou calcular e mostrar pra você daqui a pouco.

n8n 编辑字段节点参数:Mode 为 Manual Mapping,四个字段 product、qty、unit_price、freight
Quatro campos, três são números. Os nomes dos campos usam underscore em vez de camelCase, porque depois na hora de escrever o código vai precisar concatenar row.unit_price, falta um caractere e já é undefined.

Aquele na parte de baixo do painel Include Other Input Fields O switch vem ativado por padrão, o que significa "campos da entrada que eu não mexi são repassados como estão". A entrada deste fluxo está vazia, então tanto faz o switch estar ligado ou desligado. Mas num fluxo com dados, desligá-lo equivale a cortar todos os campos upstream, restando apenas os quatro que você preencheu manualmente — aí quando o código não conseguir pegar product, não pense que o código está errado.

Nó de código: pega um, calcula uma vez, devolve um

Dê um duplo clique em «Calcular valor».Mode Manter Run Once for All Items(o array inteiro corre uma vez),Language Manter JavaScript. Depois cole o trecho abaixo na área de edição:

const row = items[0].json;
const amount = row.qty * row.unit_price + row.freight;
const unitCost = Number((amount / row.qty).toFixed(2));

return [{
  json: {
    product: row.product,
    qty: row.qty,
    amount,
    unitCost,
    summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',
  },
}];

Linha a linha, só os pontos que mordem.

items[0].json É pegar o primeiro. Aqui só há um item de dados, por isso [0] chega. Cenários com vários itens exigem escrever items.map(...), isso é assunto do próximo artigo.items é a forma antiga, ainda funciona no 2.x; a forma nova é $input.first().json, as duas correram nesta instância com resultado exatamente igual. Mantenho items, porque no modo «Run Once for All Items» tens mesmo o array inteiro na mão, e este nome é mais honesto.

toFixed(2) Não é para ficar bonito. 430 a dividir por 200 dá exatamente 2.15, parece arrumado; trocando para frete 55, dá 2.175, e o cálculo em vírgula flutuante pode dar 2.1749999999999998. Escrito numa tabela ou enviado ao cliente, esse rabo é feio. A camada exterior Number() É convertê-lo de string de volta para número —toFixed O retorno é uma string; se não converter, unitCost ficará alinhado à esquerda na tabela.

O trecho final return é o ponto principal.Retorna um array, cada elemento envolto em uma camada de json. O n8n usa essa chave json para distinguir "isto é dado de negócio" de "isto é anexo binário". Se o formato estiver errado, o node fica vermelho na hora.

n8n 代码节点参数:Mode 为 Run Once for All Items,Language 为 JavaScript
O dropdown Mode tem duas opções; escolher a errada inutiliza o código inteiro. A diferença está no item 2 de "cinco erros que realmente vão acontecer" mais adiante.

Execute uma vez e veja a saída

Clique no "Executar" laranja abaixo da tela. Em um ou dois segundos, as bordas dos três nodes ficam verdes, cada node ganha um pequeno check no canto superior direito, e a conexão mostra "1 item" no meio.

n8n 三节点工作流执行完成,连线上标注 1 个项目,节点带绿色勾
O "1 item" na conexão é quantos dados passaram por aquela linha. Quando você adicionar nodes de split later, isso vira 3, 5, 10, e dá para ver de imediato em qual etapa os dados foram multiplicados.

Dê duplo clique em "Calcular valor", e na área de saída à direita mude para a aba JSON:

n8n 代码节点输出的 JSON:amount 430、unitCost 2.15 与拼好的 summary
430 é 200×1.85+60, e 2.15 é 430÷200. Conferir na mão bate, o que mostra que os três nodes estão ligados corretamente.

Aliás, um ponto que confunde facilmente: esse botão "Executar" só roda quando você clica. Um workflow com Manual Trigger não começa a rodar sozinho só porque foi publicado; ele não tem mecanismo de "rodar sozinho". Para agendamento, é preciso trocar para um trigger do tipo Schedule Trigger.

Anexo: JSON do workflow completo

Se não quiser clicar nos nodes, dá para importar o conjunto inteiro. Copie o trecho abaixo e, em uma área vazia da tela, pressione Ctrl+V, os três nós caem diretamente na tela. Também dá para usar o menu: canto superior direito ⋯ → Importar → Importar do arquivo, primeiro salve o conteúdo como .json。

Este trecho eu mesmo colei em um workflow em branco e executei uma vez: três nós, duas conexões, posições corretas, e após a execução amount também é 430.

{
  "name": "第一个工作流",
  "nodes": [
    {"parameters":{},"id":"m1","name":"手动触发","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[-368,0]},
    {"parameters":{"assignments":{"assignments":[
      {"id":"a1","name":"product","value":"真空压缩袋 60x80cm","type":"string"},
      {"id":"a2","name":"qty","value":200,"type":"number"},
      {"id":"a3","name":"unit_price","value":1.85,"type":"number"},
      {"id":"a4","name":"freight","value":60,"type":"number"}
    ]},"includeOtherFields":true,"options":{}},"id":"m2","name":"填写报价单","type":"n8n-nodes-base.set","typeVersion":3.5,"position":[-144,0]},
    {"parameters":{"jsCode":"const row = items[0].json;nconst amount = row.qty * row.unit_price + row.freight;nconst unitCost = Number((amount / row.qty).toFixed(2));nnreturn [{n  json: {n    product: row.product,n    qty: row.qty,n    amount,n    unitCost,n    summary: row.product + ' ' + row.qty + ' 个,含运费合计 ' + amount + ' 元,单个成本 ' + unitCost + ' 元',n  },n}];","mode":"runOnceForAllItems"},"id":"m3","name":"计算金额","type":"n8n-nodes-base.code","typeVersion":2,"position":[80,0]}
  ],
  "connections": {
    "手动触发": {"main": [[{"node": "填写报价单", "type": "main", "index": 0}]]},
    "填写报价单": {"main": [[{"node": "计算金额", "type": "main", "index": 0}]]}
  },
  "settings": {"executionOrder": "v1"}
}

Como saber se realmente funcionou

Quatro itens, todos verificáveis por conta própria, faltar um já não conta como concluído.

  1. Os três nós com borda verde e check, as duas conexões marcadas com "1 item". Se o número não for 1, significa que algum nó no meio alterou a quantidade, volte e verifique.
  2. No JSON de saída do nó de código, amount é 430, unitCost é 2.15.
  3. O histórico de execução registra esta execução. Execuções manuais também ficam registradas, não pense que só tarefas agendadas entram na lista.
  4. Altere freight de 60 para 100, execute novamente, amount deve virar 470. Mudar uma entrada e ver a saída acompanhar, este item é mais robusto que os três anteriores — ver 430 uma única vez pode ser coincidência.

O quarto item eu recomendo fazer sempre. Funcionar uma vez e funcionar corretamente são coisas diferentes.

Cinco erros que realmente acontecem

1. Code doesn't return items properly

Retornou uma string. No meu teste, escrevi apenas uma linha return "共 430 元";, o nó fica vermelho imediatamente e esta mensagem aparece no canto inferior direito.

n8n 代码节点返回字符串时报错:Code doesn't return items properly
Caixa vermelha, ponto de exclamação branco, uma mensagem de erro em inglês. Os dois nós anteriores ainda estão verdes, o que indica que o erro está no último.

O interessante é que,return { amount: 430 } essa forma de escrever sem envolver array, sem envolver jsonfunciona. O nó Code automaticamente adiciona a chave json e os colchetes, o comportamento está documentado. Portanto, este erro não é "formato fora do padrão", é "o que você retornou não é dado de forma alguma" — string, número, null não podem ser completados.

2. items is not defined [line 1]

O Mode foi selecionado como Run Once for Each Item, e o código ainda usa items. No modo item a item, o n8n alimenta apenas um por vez, o atual se chama $json, para obter a quantidade deve-se escrever $json.qty. A diferença entre os dois modos em uma frase: executar o array inteiro uma vez, ou executar cada item uma vez. Antes de escrever código, dê uma olhada neste dropdown.

3. Apenas um nó "Manual Trigger" é permitido por workflow

Inserir outro trigger em um workflow que já tem um trigger. Desta vez cometi um erro ainda mais tolo: colei um JSON com trigger na tela de um workflow existente, apareceu "não foi possível inserir o nó", seguido desta mensagem. Para testar comparando, abra primeiro um novo workflow e depois cole.

4. Produz uma saída undefined, sem erro

Em Edit Fields o campo chama-se unit_price, no código está escrito como row.unitPrice. Uma diferença de um caractere entre maiúsculas/minúsculas e underscore, o n8n ignora, o JS também ignora, o resultado é undefined a propagar-se até ao fim, e no summary aparece «total com frete undefined yuan». Este tipo de erro é o mais difícil de detetar, porque não fica vermelho, não faz som, apenas o número está errado.

5. amount torna-se "37060"

O tipo do campo foi definido como string. Sessenta e "60" parecem iguais em JS, mas + ao encontrar uma string muda para concatenação: 370 mais "60" dá "37060", não 430.

n8n 编辑字段里 freight 类型选成字符串后,代码输出 amount 变成 37060
Na entrada à esquerda o ícone antes de freight passou de # para T, à direita amount ficou "37060" com aspas, e unitCost passou a 185.3. O fluxo inteiro não dá erro, e ainda afirma com confiança «total 37060 yuan».

O quinto ponto é o mais valioso deste artigo. A primeira vez que obtive este número, fiquei meio minuto a olhar para 37060 até perceber que era concatenação de strings — porque o código parece impecável, o problema está no dropdown de tipo do nó anterior.

As três fronteiras do nó Code

O nó Code corre no próprio processo do n8n, mas tem sandbox: não lê ficheiros locais, não envia pedidos HTTP. Para mexer em ficheiros usa o nó Read/Write File From Disk, para enviar pedidos usa o nó HTTP Request. Isto não é uma limitação, é reduzir ao mínimo «o que um pedaço de JS colado à pressa pode fazer».

Mas há três coisas que ele não impede, e que tens de gerir tu.

Não coles JSON de workflows que outros te dão.Colar é equivalente a executar JS escrito por outros na tua própria instância. Na minha instância, colar JSON insere diretamente os nós, sem pedir confirmação, sem perguntar a origem. Se quiseres mesmo testar, mete primeiro numa instância de teste vazia, lê o código e só depois decides se corres.

Não hardcodes chaves no código.O workflow pode ser exportado inteiro, enviado a um colega, colado no grupo. A api key hardcoded no código vai junto com o JSON. O ideal é usar um nó de credenciais, para que o n8n armazene criptografado.

O self-hosting permite instalar pacotes npm de terceiros.Vem desativado por padrão, é preciso ativar manualmente a variável de ambiente. Ativar equivale a abrir uma brecha de execução de código arbitrário para o nó Code. Numa instância de uso individual, tudo bem; numa instância compartilhada por várias pessoas, recomendo manter sempre desativado.

Resumo

Três nós, uma cadeia: o trigger gera dados, o Edit Fields complementa dados, o Code calcula dados. O que realmente importa memorizar não é como clicar nesses três nós, mas a forma de «array contendo json». Depois, IF, Merge, loop, branch de erro, tudo é variação dessa forma.

Evite usar código quando puder. Esta cotação também pode ser calculada com Edit Fields mais expressões,{{ $json.qty * $json.unit_price + $json.freight }} uma linha resolve, e ainda economiza um nó. Optei pelo Code no final porque depois preciso calcular simultaneamente cotações em três critérios, e empilhar expressões no nó é mais difícil de manter do que escrever código. Se você só calcula um número, não use código.

Aquele problema de tipo, sugiro que agora mesmo mude freight para string de propósito e rode uma vez, vendo com seus próprios olhos 430 virar 37060. Ler dez vezes o texto não vale tanto quanto errar uma vez você mesmo.

Continuando adiante

O artigo anterior foi《Como escolher entre n8n, Zapier e Make》. Como nós, conexões e execuções se parecem no JSON,este artigojá detalhou. Se um dia o serviço não subir, ou a porta conflitar, veja《Como investigar erros de inicialização e ocupação de porta do n8n》. Para ver a instalaçãoeste artigo, se não estiver familiarizado com a interface, consulteeste artigo. O índice de todo o curso está emaqui。

Referências

  • Manual Trigger: só é executado quando você clica em executar, não fornece acionamento automático; um workflow só permite um
  • Edit Fields (Set): dois modos, Manual Mapping e JSON Output, a função de Include in Output
  • Code: a diferença entre os dois Mode, opções de linguagem, não pode acessar o sistema de arquivos nem iniciar solicitações HTTP
  • Understand n8n's data structure: a origem de array aninhado com json, e a explicação de que o nó Code adiciona automaticamente a chave json
  • Use atalhos integrados: variáveis e métodos integrados nas expressões, quais não se aplicam no nó Code

Última atualização: 28 de outubro de 2026, usando n8n 2.43.7.

THE END