La maggior parte dei workflow vive di “dati che passano” tra i nodi: arrivano, vengono usati, poi spariscono. Ma spesso serve qualcosa di più: un piccolo stato che sopravvive tra un’esecuzione e l’altra, contatori, cursori di paginazione, mapping che non vuoi riscaricare ogni volta. Qui entra in gioco $workflow n8n e i Dati Statici del workflow: un meccanismo leggero per memorizzare configurazioni e micro‑stati direttamente nell’automazione, senza installare database esterni. In questa guida pratica vedremo cosa sono i dati statici del workflow n8n, come usarli in sicurezza, quando preferirli (e quando no), e come comporli con le variabili di espressione n8n ($json, $node, $env) per routing, naming e logging. Troverai esempi reali (cursore API, contatori, feature flags), consigli su idempotenza e concorrenza esecuzioni n8n, e un mini‑framework per crescere senza introdurre complessità. Se vuoi migliorare produttività e affidabilità dei tuoi flussi da marketer tecnico, questa è la base per portare governance e controllo nelle tue automazioni.

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

[IMG: panoramica dello scope dati in n8n: item → node → workflow static data]

Dati transitori vs stato persistito: quando serve e perché

Nel modello a item di n8n, i dati viaggiano da un nodo all’altro sotto forma di array di oggetti JSON ($json in espressioni n8n). Questi dati sono transitori: cambiano ad ogni esecuzione. A volte però serve uno “zainetto” persistente:

  • Configurazioni persistenti workflow: piccole preferenze (formati, mapping campi) che non vuoi hardcodare nel nodo.
  • Cursore di paginazione API: l’ultimo next_page token processato; al prossimo run riparti da lì.
  • Contatori di esecuzione: quante volte hai pubblicato oggi, per canale/campagna.
  • Cache leggera in automazioni: lookup/mapping in n8n a bassa volatilità (es. ID canale → nome pubblico).
  • Idempotenza nei workflow: memorizza checksum o riferimenti di eventi già processati.

Differenze chiave

  • Dati transitori: vivono nel run corrente (item, output nodi). Perfetti per trasformazioni veloci.
  • Dati statici del workflow n8n: persistono tra run; dimensione piccola; ideali per piano di controllo e micro‑config.

Best practice n8n espressioni: separa il dato “business” (item) dal “controllo” (static data). E tieni l’input del workflow come sorgente della verità: lo stato persistito serve a ricordare “dove sei arrivato”, non a sostituire la fonte.

[IMG: tabella comparativa “transitorio vs statico” con esempi]

L’oggetto $workflow di n8n: metadati, scope e riferimenti utili

$workflow n8n è accessibile nelle espressioni e ti dà metadati dell’automazione utili per naming, logging e routing. Esempi tipici:

  • Esempi in espressioni

  • Nome workflow: {{$workflow.name}}

  • ID workflow: {{$workflow.id}}

  • Naming file/log: report{{$workflow.name}}{{ $now }}.csv

  • Tagging run: {{$workflow.id}}-{{ $env.ENVIRONMENT }}

  • Differenze con $node e $json in espressioni n8n

  • $json: il dato dell’item corrente (per esempio {{$json.email}}).

  • $node[“Nome Nodo”].json.campo: accedi a un output specifico di un nodo precedente.

  • $env: leggi variabili d’ambiente (token, ID foglio, URL API).

  • $workflow: metadati del flusso per scopi cross‑cutting (naming, logging, branch per ambiente).

Scope dei dati in n8n

  • Scope globale del workflow: utile per etichette e contesto.
  • Scope del singolo nodo: usalo per riferimenti puntuali o default di quel nodo.

Esempi d’uso concreti

  • Routing per ambiente: If → {{$env.ENVIRONMENT}} == ‘prod’ → percorsi diversi di notifica.
  • Logging coerente: Append log row → fields: workflowid: {{$workflow.id}}, executionts: {{ $now }}.
  • Convenzioni di naming file/output: s3://bucket/{{ $env.BRAND }}/{{$workflow.name}}/{{ $now }}.json

Insight: usare $workflow negli output e nelle notifiche rende ogni run auto‑documentata e facile da investigare.

[IMG: screenshot placeholder con espressioni $workflow, $env, $json nel pannello proprietà]

Pattern di persistenza: cursori, contatori, mapping e feature flags

Quando memorizzare

  • Cursore di paginazione API: salva next_page token ad ogni run concluso con successo. Se fallisce, non aggiornare (eviti “buchi”).
  • Contatori di esecuzione: mantieni un contatore giornaliero per canale (utile per rate limiting applicativo).
  • Cache di configurazioni/mapping: es. canale_id → nome; TTL logico (ricarica ogni N run/ore).
  • Feature flags e versioning di flusso: interruttori per attivare/disattivare path senza modificare i nodi.

Esempio (pseudo‑JS in un Code node) per cursore API e contatore giornaliero:

// Pseudocodice per illustrare il pattern
// Lettura stato persistito (es. oggetto staticData globale)
const state = staticData.global || {}; // fallback oggetto vuoto
const today = new Date().toISOString().slice(0,10);

// Cursore API
const previousCursor = state.nextCursor || null;
// ... chiama API usando previousCursor ...
const nextCursor = $json.apiResponse?.next_cursor || null;
if (nextCursor) state.nextCursor = nextCursor;

// Contatori giornalieri per canale
state.counts = state.counts || {};
const channel = $json.channel || 'unknown';
state.counts[today] = state.counts[today] || {};
state.counts[today][channel] = (state.counts[today][channel] || 0) + 1;

// Salvataggio
staticData.global = state;

return [{ json: { previousCursor, nextCursor, processed: 1 } }];

Note operative (ciclo di vita, limiti)

  • Mantieni lo stato piccolo (stringhe, numeri, array/dict compatti).
  • In caso di migrazione/cleanup, prevedi reset dei dati statici (svuota l’oggetto o definisci una chiave di versione, es. state.version = 2).
  • Evita dataset grandi o lookup complessi: passa a un DB/KV store esterno.

[IMG: diagramma con “API → process → update cursor → next run resumes”]

Concorrenza e idempotenza: evitare race e duplicazioni

Concorrenza esecuzioni n8n

  • Se il workflow può avviarsi in parallelo (Cron sovrapposti, trigger multipli), rischi race condition sullo stato.
  • Pattern di lock leggero: salva un lock con timestamp ed execution marker; se un’altra run lo vede “attivo”, si mette in attesa o esce.

Idempotenza nei workflow

  • Prima di eseguire side effects (email, inserimenti), verifica in uno “storico” che l’ID sorgente non sia già stato processato.
  • Usa contatori/checksum nello stato per saltare azioni ripetute.
  • Gestisci retry con backoff e segna esiti (success/fail) con motivazione per safe reprocessing.

Esempio (pseudo‑JS) per lock e idempotenza:

const state = staticData.global || {};
const now = Date.now();
const lockTTLms = 5 * 60 * 1000; // 5 minuti

// Lock
if (state.lock && (now - state.lock.ts) < lockTTLms) {
  // lock attivo: abbandona o riprova più tardi
  return [{ json: { skipped: true, reason: 'locked' } }];
}

// Acquisisci lock
state.lock = { ts: now };
staticData.global = state;

// ... esegui lavoro idempotente ...
// es: se vedi source_event_id in state.processed, salta
state.processed = state.processed || {};
const evt = $json.source_event_id;
if (evt && state.processed[evt]) {
  return [{ json: { skipped: true, reason: 'already_processed' } }];
}

// Esegui side effect e marca come processato
// ... side effect ...
state.processed[evt] = true;
delete state.lock;
staticData.global = state;

return [{ json: { ok: true } }];

Suggerimenti

  • Se hai traffico elevato o necessità di garanzie forti, usa un KV store/DB con lock atomico (redis, postgres) e conserva lo stato di idempotenza lì.
  • Per piccoli team e throughput basso, il lock leggero in dati statici è sufficiente.

[IMG: timeline con due esecuzioni concorrenti e lock che evita conflitti]

Espressioni e $workflow in pratica: routing, naming, logging

Esempi pratici (copiabili):

  • Routing per canale/ambiente
  • If → Condizione: {{$env.ENVIRONMENT}} === ‘prod’ && {{$json.channel}} === ‘linkedin’
  • True branch: pubblica; False: logga come “dry‑run”.
  • Naming coerente per export
  • File Name: {{ $env.BRAND }}{{ $workflow.name }}{{ new Date().toISOString().slice(0,10) }}.csv
  • Logging strutturato (Append Row su Google Sheets o DB)
  • Fields: workflowid: {{$workflow.id}}, runts: {{ $now }}, item_id: {{$json.id}}, outcome: {{$json.status}}

Pattern per lookup/mapping in n8n

  • Se mapping statico è piccolo (es. codice → canale), conservalo nei dati statici e leggilo con un Code node in avvio run.
  • Aggiorna il mapping in modo controllato (flag di versione; se cambia, ricalcola al primo run).

Differenze pratiche tra $workflow, $node, $json e $env (sintesi)

  • $json: payload dell’item corrente.
  • $node: referenzia output di altri nodi (utile per join/merge).
  • $env: configurazioni segrete/ambienti (non committare nel workflow).
  • $workflow: metadati; usalo per contesto, naming, tag.

[IMG: pannello proprietà con esempi di espressioni interpolate nei campi]

Quando NON usare i Dati Statici e alternative scalabili

Evita i Dati Statici se:

  • Dataset grandi o query complesse: hai bisogno di filtri, ordinamenti, ricerche—usa un DB/warehouse o un KV store.
  • Team concurrency: più utenti/istanze modificano lo stato; rischi conflitti. Meglio un archivio centralizzato con lock transazionali.
  • Compliance/audit: serve storico delle modifiche e tracciabilità nel tempo—usa storage versionato.

Alternative consigliate

  • DB esterno (Postgres/MySQL): per cursori per sorgente, idempotenza forte, audit.
  • KV store (Redis): rate limiting, lock, cache a TTL.
  • File/Blob (S3/Drive): export periodici e snapshot dello stato di controllo.
  • API di configurazione: mantieni le config in un servizio dedicato e leggile nel workflow.

Pulizia/reset dei Dati Statici

  • Prevedi un percorso “Reset”: un Code node che azzera lo stato o incrementa una version key, forzando rigenerazione al prossimo run.
  • Migrazioni tra versioni e ambienti: serializza lo stato in JSON, salvalo temporaneamente, poi re‑importalo con trasformazioni minime.

[IMG: decision tree “static data vs DB/KV” con criteri di scelta]

Esempio end‑to‑end: ingest API paginata con cursore, dedupe e log

Obiettivo: chiamare un’API paginata, ripartire dall’ultimo cursore, deduplicare item per id, e loggare risultato.

Blueprint
1) Trigger: Cron ogni 15 minuti.
2) Carica stato: Code node legge state (cursore, processed ids).
3) Chiamata API: HTTP Request → usa previousCursor.
4) Process: dedupe su item_id contro state.processed.
5) Persisti: aggiorna cursore se la chiamata è ok, marca nuovi id processati.
6) Log: Append su Sheet/DB con conteggi.

Pseudo‑JS (Code node “State Manager”):

const state = staticData.global || { version: 1, processed: {}, nextCursor: null };
const prev = state.nextCursor || null;
return [{ json: { state, prevCursor: prev } }];

Chiamata API (HTTP Request) usa {{ $json.prevCursor }} in query; a valle:

const state = $json.state;
const items = $json.apiResponse?.data || [];
const out = [];
for (const it of items) {
  if (!state.processed[it.id]) {
    out.push(it);
    state.processed[it.id] = true;
  }
}
state.nextCursor = $json.apiResponse?.next_cursor || state.nextCursor;
staticData.global = state;
return out.map(i => ({ json: i }));

Questo pattern offre persistenza stato automazioni n8n senza DB, con deduplica semplice e ripartenza affidabile.

[IMG: workflow canvas con nodi: Cron → Code(State) → HTTP → Code(Process) → Sheets(DB)]

Quick Takeaways

  • Usa $workflow per metadati e contesto: naming coerente, logging e routing per ambiente.
  • I Dati Statici sono perfetti per cursori, contatori, mapping piccoli e feature flags.
  • Mantieni lo stato leggero e versionato; per dataset grandi passa a DB/KV esterni.
  • Gestisci concorrenza con lock leggeri e idempotenza su side effects.
  • Combina $json, $node, $env e $workflow per espressioni potenti e leggibili.
  • Prevedi reset/migrazione dello stato e monitora con log strutturati.

Conclusione

Portare stabilità e

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