Hai già creato qualche flusso, ma ti serve più controllo sulla forma dei dati? Con il nodo code n8n puoi modellare, aggregare e validare i payload in modo preciso, lì dove i nodi no‑code non bastano. In questa guida operativa andiamo oltre le basi: vedrai quando ha senso superare i nodi standard, come scegliere tra esecuzione per item e per tutti gli item, come leggere input/contesto e restituire output corretti, come progettare uno schema stabile per il downstream e implementare pattern avanzati (flatten/unflatten, groupBy, join/lookup, deduplicazione). Tratteremo anche performance (chunking, batching), differenze pratiche tra JavaScript e Python, considerazioni su moduli esterni in ambienti self‑hosted e Cloud, e un approccio solido a debug e gestione errori nel Code node. L’obiettivo: padroneggiare la trasformazione dati in n8n con snippet pronti all’uso e linee guida che rendono i tuoi workflow più affidabili, veloci e manutenibili.

📚 Nuovo a n8n? Parti dalla guida completa: cos'è n8n e come funziona.

[IMG: Editor con nodo “Code” selezionato e menu “Run Once For Each Item / Run Once For All Items” visibile]


Quando superare i nodi no‑code (e perché il Code è la scelta giusta)

I nodi no‑code coprono l’80% dei casi (HTTP Request, Set, IF, Merge, Item Lists, Split In Batches). Ma in alcuni scenari il Code node di n8n è la scorciatoia più pulita:

  • Data shaping non coperto: rinomina massiva di campi, conversioni di tipi, calcoli custom, regole condizionali complesse.
  • Normalizzazioni multi‑fonte: uniformare diversi payload API a uno schema comune prima di un Merge.
  • Filtri/ordinamenti/aggregazioni complessi: preferisci un “punto unico” di verità con codice chiaro anziché proliferare di IF/Switch.

Principio guida:

  • Parti dai nodi standard (meno codice = meno debito tecnico).
  • Inserisci il Code quando hai un “collo di bottiglia” di trasformazione: di solito 20‑60 righe di JS bastano per risolvere in modo espressivo.

Limitazioni da ricordare:

  • Il Code non fa I/O di rete o file system: per chiamate esterne usa “HTTP Request” e passa i risultati al Code.
  • Mantieni lo scope del Code “puro” sui dati: più testabile, meno side‑effect.

Risultato: workflow più leggibili, dove il nodo code n8n svolge un ruolo mirato e documentato, senza sostituire ciò che i nodi nativi già fanno bene.


Esecuzione per item vs per tutti gli item: pattern, trade‑off e insidie

Il Code node supporta due modalità chiave.

1) Run Once For Each Item

  • Processa un item alla volta.
  • Ideale per mapping di campi, calcoli puntuali, validazioni locali.
  • Output atteso: un singolo item (oggetto con chiave json).

Esempio (per‑item, mapping + calcolo):

// Mode: Run Once For Each Item
const r = $json;
return {
  json: {
    id: r.id,
    title: String(r.title || r.name || '').trim(),
    amount: Number(r.qty || 0) * Number(r.unitPrice || 0),
    createdAt: r.createdAt || r.created_at || null,
  }
};

2) Run Once For All Items

  • Processa tutti gli item insieme con $input.all().
  • Perfetto per filtri globali, ordinamenti, dedup, groupBy, aggregazioni.
  • Output atteso: un array di item (return items) oppure un singolo item aggregato ({ json: {…} }).

Esempio (all‑items, filtro + sort + limit):

// Mode: Run Once For All Items
const rows = $input.all().map(i => i.json);
const filtered = rows.filter(r => r.status === 'published');
filtered.sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt));
const top = filtered.slice(0, 50);
return top.map(j => ({ json: j }));

Trade‑off pratici:

  • Per‑item preserva la cardinalità 1:1 (facile da ragionare).
  • All‑items è più flessibile ma richiede attenzione al formato di ritorno. Se filtri, restituisci solo gli item che restano (“return top.map(…)”).

Insidia tipica: restituire il formato sbagliato. Ricorda:

  • Per‑item: return { json: {…} }
  • All‑items: return [{ json: {…} }, …] oppure return items;

Input, contesto e output coerente: item linking e struttura degli item

Struttura degli item

  • Ogni messaggio è un item: { json: { … }, binary?: { … } }.
  • I nodi scambiano array di item: anche se c’è un solo elemento, è un array di length 1.

Accesso ai dati (built‑in)

  • $json: il JSON del corrente item (in per‑item).
  • $input.all(): tutti gli item in ingresso (in all‑items).
  • $node[“NomeNodo”].json: leggi il json di un nodo precedente per disambiguare fork.
  • $items(): riferimento a più item in contesti particolari.

Esempio (all‑items, arricchire gli item con un campo di provenienza):

const items = $input.all();
const enriched = items.map(it => {
  const j = it.json;
  return { json: { ...j, _sourceNode: $node["HTTP Request"].name || "unknown" } };
});
return enriched;

Item linking: unire dataset

  • Usa il nodo “Merge” (ad esempio “Merge By Key”) per allineare stream diversi sulla stessa chiave (es. id cliente).
  • Nel Code, preparati a ricevere campi combinati e a gestire conflitti di nomi (prefissi o rinomina).

Output coerente

  • Mantieni i nomi dei campi stabili e “case‑sensitive”.
  • Preserva gli identificatori chiave (id, guid) per tracciabilità.
  • Quando fai aggregazioni, valuta se restituire:
  • un array di item (downstream “per‑record”),
  • un singolo item aggregato (downstream “riassunto”).

[IMG: Pannello “Input/Output” del nodo Code con array di item e campi json evidenziati]


Normalizzazione dello schema: flatten/unflatten, mapping e default sicuri

Progettare uno schema stabile

  • Definisci i tipi per campo (string, number, boolean, date ISO) e rispetta la scelta in tutto il flusso.
  • Segna i campi opzionali e fornisci default sicuri (stringa vuota, 0, null).

Flatten JSON e unflatten

  • Utile per esporti tabellari (CSV/Sheets) o per semplificare mapping.

Esempio (per‑item, flatten profondo):

function flatten(obj, prefix = '', out = {}) {
  for (const [k, v] of Object.entries(obj || {})) {
    const key = prefix ? `${prefix}.${k}` : k;
    if (v && typeof v === 'object' && !Array.isArray(v)) {
      flatten(v, key, out);
    } else {
      out[key] = v;
    }
  }
  return out;
}

const f = flatten($json);
return { json: f };

Rinomina massiva e mapping enum

// Per-item: rinomina campi e mappa enum in valori standard
const j = $json;
const statusMap = { active: 'ACTIVE', pending: 'PENDING', disabled: 'DISABLED' };
return {
  json: {
    id: j.id || j._id,
    email: String(j.email || '').toLowerCase().trim(),
    status: statusMap[j.status] || 'UNKNOWN',
    amount: Number(j.amount ?? 0),
    createdAt: j.createdAt || j.created_at || null,
  }
};

Default sicuri e null‑safety

  • Usa coalescenza: j.field ?? default per distinguere tra null/undefined e 0/false validi.
  • Pulisci stringhe con trim(), normalizza maiuscole/minuscole, formatta numeri e date coerentemente.

Benefici: downstream prevedibile, meno condizioni speciali, integrazione più fluida con nodi come “Spreadsheet File” o “Google Sheets”.


Aggregazioni avanzate, groupBy, join/lookup e deduplicazione

GroupBy e aggregazioni in n8n

// All-items: somma vendite per categoria
const rows = $input.all().map(i => i.json);
const agg = {};
for (const r of rows) {
  const k = r.category || 'Uncategorized';
  agg[k] = (agg[k] || 0) + Number(r.amount || 0);
}
const out = Object.entries(agg).map(([category, total]) => ({ json: { category, total } }));
return out;

Finestre temporali e pivot leggeri

  • Filtra per data (es. ultimi 30 giorni), poi aggrega.
  • Crea piccole “pivot” (es. conteggi per stato) restituendo un oggetto riassunto in un singolo item.

Join/lookup tra dataset

  • Preferisci il nodo “Merge” con “Merge By Key” quando possibile.
  • In Code, fai lookup con Map/oggetto indicizzato:
// All-items: join sinistro tra arrA e arrB per key=id
const A = $input.all().map(i => i.json);  // stream A
const B = $node["Normalize B"].json.rows; // es. passato da nodo precedente
const idx = new Map(B.map(b => [b.id, b]));
const joined = A.map(a => ({ ...a, match: idx.get(a.id) || null }));
return joined.map(j => ({ json: j }));

Deduplicazione record n8n (per chiave)

// All-items: dedup per email
const inRows = $input.all().map(i => i.json);
const seen = new Set();
const deduped = [];
for (const r of inRows) {
  const key = String(r.email || '').toLowerCase().trim();
  if (!key || seen.has(key)) continue;
  seen.add(key);
  deduped.push(r);
}
return deduped.map(r => ({ json: r }));

Risultato: dataset compatti, coerenti e pronti per esportazione o analisi, con logiche di business incapsulate in pochi snippet.


Performance, grandi volumi e scelte runtime (JS/Python, moduli esterni)

Performance del Code node

  • Complessità: preferisci passate lineari (O(n)), evita nidificazioni costose. Per join, indice prima (Map) e poi scorri.
  • Chunking/batching: abbina “Split In Batches” per processare gruppi (es. 100/500 item), inserendo attese tra batch se richieste da rate limit a valle.

Pattern batching

  • Pre: sorgente produce array grande → “Item Lists” per splittare in item singoli.
  • Process: “Split In Batches” → Code/HTTP → “Execute Next Batch”.
  • Post: ricomposizione se serve, oppure esportazione diretta.

Scelta tra JavaScript e Python

  • JavaScript: default, veloce, integrazione stretta con $json, $input.all(), return items; ottimo per trasformazioni generiche.
  • Python: utile per calcoli specifici, ma verifica il supporto runtime; in molti casi JS “vanilla” copre l’esigenza con meno attrito.

Moduli esterni in ambiente self‑hosted e Cloud

  • Self‑hosted: puoi avere maggiore controllo su dipendenze e ambienti.
  • Cloud: adotta le capacità di base del Code; per librerie complesse preferisci nodi dedicati o sub‑workflow/servizi esterni.
  • Regola d’oro: se serve una libreria “pesante”, considera di spostare la logica in un servizio o usare nodi nativi che ottimizzano I/O e retry.

[IMG: Canvas con “Item Lists → Split In Batches → Code → HTTP Request → Execute Next Batch”]


Debug efficace e contratti di schema: prevenire errori prima che succedano

Debug e gestione errori nel Code node

  • Log mirati: console.log() con conteggi, chiavi e range; rimuovi i log a fine debug.
  • try/catch con messaggi chiari:
try {
  const items = $input.all().map(i => i.json);
  if (!Array.isArray(items)) throw new Error('Input non array');
  // ... trasformazioni
  return items.map(j => ({ json: j }));
} catch (e) {
  throw new Error(`Code node: ${e.message}`);
}

Contratti di schema

  • Documenta input/output attesi del Code: campi, tipi, opzionalità.
  • Valida e sanitizza all’ingresso: default, trim, cast dei tipi.
  • A valle, usa “IF” per intercettare record invalidi e deviare verso un ramo di correzione o log.

Da JSON annidato a tabella CSV

// Per-item: selezione e ordering campi per export
const j = $json;
const row = {
  id: j.id,
  email: (j.email || '').toLowerCase(),
  country: j.address?.country || '',
  total: Number(j.total ?? 0),
  createdAt: j.createdAt || null,
};
return { json: row };

Merge eventi con finestra temporale

// All-items: dedup per key + finestra recente
const now = Date.now();
const windowMs = 1000 * 60 * 60 * 24 * 7; // 7 giorni
const rows = $input.all().map(i => i.json).filter(r => {
  const t = new Date(r.timestamp || r.createdAt || 0).getTime();
  return !isNaN(t) && (now - t) <= windowMs;
});
const seen = new Set();
const out = [];
for (const r of rows) {
  const key = r.guid || r.id || r.url;
  if (!key || seen.has(key)) continue;
  seen.add(key);
  out.push(r);
}
return out.map(r => ({ json: r }));

Suggerimenti finali:

  • Nomina coerente (snake/camel) e commenti orientati al flusso.
  • Snippet riutilizzabili: crea un “catalogo” interno di ricette (flatten, groupBy, join, dedup).

Quick Takeaways

  • Scegli la modalità giusta: per‑item per mapping/local, all‑items per filtri/ordinamenti/aggregazioni/dedup.
  • Rispetta la struttura: item = { json, binary? }; output coerente con return { json } o return items.
  • Normalizza presto: tipi, default, rinomina massiva e, se serve, flatten/unflatten.
  • Per join/lookup usa “Merge By Key” o indici in Map; per dedup scegli una chiave stabile (email/id/guid).
  • Scala con Item Lists + Split In Batches; riduci complessità con passate lineari e indici.
  • Debug disciplinato (log, try/catch) e contratti di schema evitano regressioni e sorprese a valle.

Conclusione

Il nodo code n8n è il perno della trasformazione dati in n8n quando servono controllo e precisione: ti permette di modellare payload eterogenei in uno schema stabile, aggregare e deduplicare con pochi passaggi, e preparare i dati per nodi downstream o esportazioni. Scegliendo con cura tra esecuzione per item e per tutti gli item, rispettando la struttura degli item e i contratti di schema, riduci drasticamente bug e fragilità. Con pattern come flatten/unflatten, groupBy, join/lookup e dedup, affronti i casi avanzati senza appesantire il canvas. E grazie a batching, codice “vanilla” e debug mirato, mantieni performance e manutenibilità anche su volumi crescenti. Per i marketer, questo si traduce in automazioni più affidabili e insight più puliti, con meno tempo speso a “ripulire” i dati e più tempo dedicato a decisioni e risultati. Inizia dagli snippet e dai pattern di questa guida: il tuo prossimo workflow avrà dati più chiari e un downstream più felice.


FAQ

1) Quando conviene passare dal no‑code al Code node di n8n?
Quando devi applicare trasformazioni non coperte dai nodi standard: mapping massivo, normalizzazioni complesse, filtri/ordinamenti avanzati o aggregazioni custom. Mantieni però HTTP/FS nei nodi dedicati.

2) Come scelgo tra esecuzione per item vs tutti gli item?
Usa per‑item per modifiche 1:1 e all‑items per operazioni globali (filtri, sort, dedup, groupBy). Ricorda il formato: per‑item return { json }, all‑items return items.

3) Come implemento una deduplicazione affidabile?
Definisci una chiave stabile (email/id/guid). In all‑items, usa un Set per tracciare le chiavi viste e restituisci solo i record unici. Se necessario, limita a una finestra temporale.

4) Posso fare join/lookup senza scrivere molto codice?
Sì, con il nodo “Merge” e modalità “Merge By Key”. In alternativa, indicizza il dataset secondario in una Map e arricchisci gli item primari in un Code all‑items.

5) Come ottimizzo performance su grandi volumi?
Evita complessità elevate: indicizza prima, scorri poi. Usa “Item Lists” per splittare array e “Split In Batches” per processare chunk, con piccole attese se imposte da rate limit a valle.


Ci dai una mano?

Quale pattern userai per primo nel tuo prossimo flusso: dedup per chiave, join con Merge o normalizzazione con flatten? Se ti è stato utile, condividi questo articolo con il team e raccontaci nei commenti il tuo caso d’uso: arricchiremo la raccolta di snippet per la community!

Vuoi automazioni AI su misura per la tua azienda?
Scopri la consulenza →