Le automazioni crescono di valore quando puoi arricchire la logica con funzioni mature, testate e veloci. Ecco perché integrare librerie esterne nel nodo Code di n8n sblocca casi d’uso avanzati (pulizia dati, firma HMAC, validazioni complesse, normalizzazioni, AI helpers) senza costruire custom nodes. In questa guida pratica su librerie esterne nodo code n8n vedrai come valutare i limiti del sandbox, aggiungere moduli npm in self‑hosted via Docker in modo persistente, impostare NODE_PATH per la risoluzione dei moduli, creare bundle con esbuild/webpack quando non puoi toccare l’immagine, e quando conviene delegare a microservizi esterni con l’HTTP Request node. Approfondiremo anche compatibilità CommonJS/ESM, pattern di importazione, performance e sicurezza (version pinning, audit), e un troubleshooting mirato per “Cannot find module” e mismatch ESM/CommonJS. L’obiettivo: fornirti ricette funzionanti e ripetibili, così trasformi il Code node in un acceleratore affidabile senza rallentare i tuoi rilasci.

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

[IMG: panoramica del workflow con Code Node che usa lodash → trasformazione → output validato]

Limiti e capacità del Code node

Il Code node (erede del Function node) esegue JavaScript su items in ingresso o può generare dati. Lavora con item.json come struttura principale e restituisce items al flusso. È perfetto quando i nodi “no‑code” non bastano. Tuttavia, l’uso di moduli npm nel nodo Code dipende da come è configurato l’ambiente:

  • Sandbox del Code node e permessi

  • In ambienti self‑hosted hai controllo su file system/immagine e puoi predisporre moduli installati e risolvibili da require.

  • In ambienti gestiti/cloud, l’uso di require/import per librerie esterne può essere limitato o non disponibile: considera bundle o microservizi esterni.

  • Cosa è consentito

  • Codice JS che manipola items e usa variabili/expressions di n8n.

  • Accesso a variabili d’ambiente via $env (es. chiavi HMAC).

  • require/import di moduli solo se disponibili sul runtime della tua istanza e nel percorso di risoluzione.

  • Differenze tra self‑hosted e cloud

  • Self‑hosted: puoi installare moduli, impostare NODE_PATH e montare volumi persistenti.

  • Cloud: prediligi soluzioni senza installazioni (bundle unico, oppure microservizi esterni).

Snippet base (Code node) — trasformazione items:

// Esegue su tutti gli items in ingresso
const items = $input.all();
const out = items.map((i) => {
  // Esempio: normalizza email e aggiunge timestamp
  const email = (i.json.email || '').trim().toLowerCase();
  return { json: { ...i.json, email, ts: $now } };
});
return out;

[IMG: Code Node con input items e anteprima output]

Aggiungere pacchetti npm in self‑hosted (metodo consigliato)

In self‑hosted, la strada più pulita è predisporre i moduli nella tua immagine/volume e consentire al Code node di risolverli via require.

Dockerfile minimale con persistenza dei pacchetti

Obiettivo: installare moduli in un percorso persistente e rendere disponibile la risoluzione dei moduli al runtime del Code node.

Dockerfile:

FROM n8nio/n8n:latest

USER root

# Crea directory persistente per i moduli
RUN mkdir -p /data/node_modules

# Installa i pacchetti npm in /data (andrà a /data/node_modules)
RUN npm install --prefix /data lodash dayjs

# Rende i moduli risolvibili da require()
ENV NODE_PATH=/data/node_modules
ENV NODE_OPTIONS=--require=module

# Permessi e ritorno all'utente non privilegiato
RUN chown -R node:node /data
USER node

docker-compose.yml (estratto):

services:
  n8n:
    build: .
    environment:
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - NODE_PATH=/data/node_modules
    volumes:
      - n8n_data:/home/node/.n8n
      - node_modules_data:/data/node_modules
    ports:
      - "5678:5678"
volumes:
  n8n_data:
  node_modules_data:

Perché funziona:

  • persistendo /data/node_modules i pacchetti non spariscono tra i rilasci;
  • NODE_PATH fa sì che require(‘lodash’) li trovi senza percorsi assoluti;
  • resti indipendente dalla global install.

Nel Code node:

const _ = require('lodash');
const dayjs = require('dayjs');

const items = $input.all();
return items.map((i) => {
  const titleCased = _.startCase((i.json.title || '').toLowerCase());
  const when = dayjs($now).toISOString();
  return { json: { ...i.json, title: titleCased, processedAt: when } };
});

[IMG: pannello esecuzione con require() riuscito e output arricchito]

Montare nodemodules come volume e configurare NODEPATH

Se non vuoi buildare un’immagine:

  • Prepara i pacchetti localmente: npm i –prefix ./runtime lodash dayjs
  • Monta ./runtime/nodemodules nel container su /data/nodemodules
  • Esporta NODEPATH=/data/nodemodules

docker-compose.yml (alternativa):

services:
  n8n:
    image: n8nio/n8n:latest
    environment:
      - NODE_PATH=/data/node_modules
    volumes:
      - ./runtime/node_modules:/data/node_modules:ro
      - n8n_data:/home/node/.n8n
    ports: ["5678:5678"]
volumes:
  n8n_data:

Nota: :ro per evitare modifiche runtime; aggiorna i pacchetti ricostruendo localmente.

Soluzioni senza modificare l’immagine

Quando non puoi toccare l’immagine di n8n, ricorri a bundle o a microservizi.

Creare un bundle locale con esbuild/webpack e importarlo nel Code node

Strategia: impacchetta la tua logica (e dipendenze) in un unico file JavaScript CommonJS, monta il file nel container e require quel bundle.

1) Crea src/index.js:

const _ = require('lodash');
const dayjs = require('dayjs');

function normalize(record) {
  const title = _.startCase((record.title || '').toLowerCase());
  return { ...record, title, processedAt: dayjs().toISOString() };
}

module.exports = { normalize };

2) Bundla con esbuild:

npx esbuild src/index.js --bundle --platform=node --format=cjs --outfile=bundle.cjs

3) Monta bundle.cjs nel container, es. /data/bundles/bundle.cjs:

services:
  n8n:
    image: n8nio/n8n:latest
    volumes:
      - ./bundles:/data/bundles:ro

4) Usa il bundle nel Code node:

const lib = require('/data/bundles/bundle.cjs');
const items = $input.all();
return items.map((i) => ({ json: lib.normalize(i.json) }));

Vantaggi: niente install globale, dipendenze “chiuse” nel bundle, portabilità tra ambienti (anche cloud se puoi montare file).

Microservizi esterni e HTTP Request node

Se il runtime non può caricare librerie:

  • sposta la logica in un microservizio (Node.js/Cloud Run/Lambda)
  • esponi un endpoint /transform
  • invoca da n8n con “n8n-nodes-base.httpRequest”

Esempio richiesta:

{
  "name": "Transform via Service",
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 1,
  "parameters": {
    "url": "https://svc.example.com/transform",
    "method": "POST",
    "responseFormat": "json",
    "jsonParameters": true,
    "bodyParametersJson": "={{ $json }}"
  }
}

Pro: totale controllo su dipendenze, scaling e tempi; Contro: gestione di un servizio in più. Ottimo per librerie pesanti (ML/NLP) o ESM‑only complesse.

[IMG: canvas con Code leggero → HTTP Request → risposta trasformata]

CommonJS vs ESM: come importare correttamente in n8n

Le librerie npm possono essere CommonJS (require/module.exports) o ESM (import/export). In ambienti NodeJS misti, segui questi pattern:

  • Pacchetto CJS:
  const slugify = require('slugify');
  • Pacchetto ESM‑only (quando require fallisce):

  • Opzione 1: transpila/bundla in CJS (esbuild/webpack) e usa require sul bundle

  • Opzione 2: import dinamico (se supportato nel runtime):

    const { default: slugify } = await import('slugify');
    

    Nota: richiede supporto a top‑level await; se non disponibile, incapsula in funzione async.

  • Pacchetti dual (module + main):

  • Prova require; se vedi errori tipo “Must use import to load ES Module”, passa a bundling o import dinamico.

  • Import named vs default:

  • Quando transpili, alcuni default finiscono in .default:
    javascript
    const pkg = require('some-esm-bundled');
    const fn = pkg.default || pkg;

In caso di dubbio e per stabilità tra ambienti, preferisci il bundle CJS.

[IMG: tabella CJS vs ESM con pattern di importazione corretti]

Sicurezza, performance e troubleshooting

Integrare librerie è utile, ma fallo in modo sicuro e performante.

  • Sicurezza dipendenze npm in automazioni

  • version pinning: fissa versioni nel package.json o nello script di build

  • audit: esegui “npm audit” e monitora CVE

  • principio del minimo set: evita pacchetti superflui (riduci superficie d’attacco)

  • segreti: non serializzare token nei log del Code node; usa $env

  • Performance del Code node con librerie esterne

  • bundle “slim”: escludi feature inutili (tree‑shaking con esbuild)

  • caching: se computi mapping ripetitivi, valuta memoization lato microservizio

  • cold start: pre‑carica bundle in volumi read‑only per ridurre I/O

  • Troubleshooting rapido

  • errore Cannot find module in n8n:

    • verifica che il pacchetto sia presente nel container/volume
    • assicurati che NODEPATH punti a /data/nodemodules (o percorso corretto)
    • controlla i permessi del volume (lettura per l’utente “node”)
  • ESM/CommonJS mismatch:

    • converti a CJS via bundle
    • usa import dinamico con default/named correnti
  • require di percorso assoluto:

    • preferisci require(‘pkg’) se NODE_PATH è impostato
    • evita path “magici” hardcoded instabili tra ambienti

Snippet di test (Code node):

try {
  const _ = require('lodash');
  return [{ json: { ok: true, lodash: !!_ } }];
} catch (e) {
  return [{ json: { ok: false, error: e.message } }];
}

[IMG: log del test con esito ok e tempi di esecuzione]

Quick Takeaways

  • In self‑hosted, installa moduli in /data/nodemodules, rendili persistenti e imposta NODEPATH per require stabile.
  • Se non puoi toccare l’immagine, crea un bundle CJS con esbuild/webpack e importalo dal Code node.
  • Per librerie pesanti o scenari ristretti, delega a microservizi e usa “n8n-nodes-base.httpRequest”.
  • Gestisci ESM vs CJS: preferisci bundle CJS per ambienti eterogenei; in alternativa usa import dinamico.
  • Migliora sicurezza con version pinning, audit e dipendenze minime; non loggare segreti.
  • “Cannot find module” si risolve verificando presenza moduli, NODE_PATH e permessi del volume.

Conclusione

Portare librerie esterne nodo code n8n in produzione è un moltiplicatore di produttività: puoi riusare anni di ecosistema npm per pulire dati, validare input, firmare richieste e orchestrare logiche avanzate senza creare custom nodes. In self‑hosted, la soluzione più solida è predisporre /data/nodemodules persistente con NODEPATH configurato; quando non è possibile, ricorri a bundle CJS portabili o a microservizi esterni richiamati dal nodo HTTP Request. Governare CommonJS/ESM con pattern chiari, fare pin delle versioni, auditare i pacchetti e contenere la dimensione delle dipendenze rende i workflow più veloci, sicuri e manutenibili. Se sei un marketer che vuole imparare ad usare n8n per migliorare la propria produttività, inizia da un caso concreto (pulizia anagrafiche, arricchimento contenuti, normalizzazione lead), crea un bundle leggero con le funzioni che ti servono e misurane l’impatto in termini di tempo risparmiato e riduzione errori. Una volta stabilizzato il pattern, standardizzalo in tutti i flussi: meno copia‑incolla, più risultati.

FAQ

  • Posso usare qualsiasi pacchetto npm nel Code node?

  • Sì, in self‑hosted puoi usare moduli npm nel nodo Code di n8n se sono installati e risolvibili (NODE_PATH corretto). In cloud, preferisci bundle o microservizi se require/import non è disponibile.

  • Come risolvo “Cannot find module”?

  • Verifica che il pacchetto sia installato nel container/volume, che NODEPATH punti a /data/nodemodules e che i permessi consentano la lettura. In caso di ESM‑only, bundla in CJS o usa import dinamico.

  • E se la libreria è ESM‑only?

  • Usa un bundle con esbuild/webpack per produrre un file CJS utilizzabile con require. In alternativa, prova import dinamico con default/named export corretti.

  • È più sicuro bundlare o installare nel container?

  • Dipende dal contesto: il bundle riduce superficie e semplifica la portabilità; l’install nel container è comodo ma richiede più controlli (audit, pin versioni). In entrambi i casi: version pinning e audit regolari.

  • Quando conviene un microservizio esterno?

  • Se le dipendenze sono pesanti, cambiano spesso o l’ambiente non consente require/import. Un microservizio isolato scala meglio e riduce l’impatto sull’istanza n8n; invocalo con il nodo “n8n-nodes-base.httpRequest”.

Hai un caso d’uso specifico in cui vorresti usare una libreria ma non sai quale strada scegliere (install, bundle o microservizio)? Raccontalo nei commenti e condividi la guida con il tuo team: quale libreria ti farebbe risparmiare più tempo questa settimana?

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