node-excel-import

Importazione di dati da file Excel/CSV.
data-io
/excel-importauth: nginx

node-excel-import

In sintesi

Microservizio di importazione massiva di dati tabellari (Excel xlsx/xls e CSV) verso il database del tenant ditta. Espone una pipeline asincrona — upload → parsing → validazione → trasformazione → arricchimento → persistenza — con tracking dello stato del job (pendingprocessingcompleted/failed) e supporto a lookup table riusabili per mapping codici. Nel grafo dei servizi è la porta d'ingresso "ETL leggero" per popolare risorse PostgreSQL a partire da fogli di calcolo prodotti dagli operatori o da sistemi esterni.

Funzionalità principali

  • Upload e parsing di file xlsx, xls e csv via multipart/form-data, con selezione del foglio, salto righe header, limite righe e validazione schema tramite Zod
  • Preview senza commit: ispezione dei primi N record (max 100) e dei nomi dei fogli, utile al frontend per costruire il mapping colonne prima dell'import effettivo
  • Column mapping dichiarativo da colonna sorgente a campo target, con flag required per i campi obbligatori
  • Trasformazioni dati componibili in catena (uppercase, lowercase, trim, parse_number, parse_date, format_date, format_currency, round, regex_replace, default_value, required, conditional, lookup, formula) applicate per ordine configurabile
  • Lookup table persistite su PostgreSQL con CRUD completo, ricerca exact match e fuzzy match (distanza di Levenshtein con estensione pg_trgm)
  • Risoluzione External ID: una colonna del foglio può fungere da chiave naturale; il servizio costruisce in batch il mapping External ID → primary key per decidere automaticamente fra INSERT e UPDATE (upsert)
  • Data enrichment opzionale verso provider HTTP esterni configurabili, con throttling a batch per non saturare i servizi remoti
  • Job tracking completo: contatori processedRows/totalRows/rowsCreated/rowsUpdated/rowsFailed, errori e timestamp, interrogabili per id, per stato o come elenco dei più recenti
  • Auto-migration PostgreSQL idempotente al bootstrap, con pg_advisory_lock per gestire più repliche sullo stesso schema

Architettura

Stack: Fastify v5 · Inversify (DI con constructor injection e decoratori @injectable()/@inject()) · @fastify/multipart per gli upload · exceljs per parsing Excel · zod per la validazione di request e righe importate · date-fns per le trasformazioni temporali · pg (driver nativo) verso PostgreSQL · @pzeta/fastify-utils (loggingPlugin, errorHandlerPlugin, healthcheckPlugin, metricsPlugin, openapiPlugin, securityPlugin con rate limit) · @pzeta/log per logging strutturato e correlation ID.

Layout DDD (src/):

LayerContenuto
Domain/Entità (ImportJob, LookupTable, ValidationResult), value object (ImportJobId, FileName, ColumnMapping, TransformationRule), interfacce repository
Application/ImportService (facade), ImportOrchestrator (pipeline), ImportJobService (lifecycle stati), FileSystemService, LookupTableService, DTO e port (IExcelParser, IDataTransformer, IDataEnrichmentService)
Infrastructure/Adapter ExcelParser (exceljs + Zod), DataTransformer (catena di trasformazioni), DataEnrichmentService (HTTP), repository PostgreSQL (PostgresImportJobRepository, PostgresLookupTableRepository, PostgresExternalIdRepository, GenericResourceRepository, PostgresMetadataRepository), PostgresConnection, AutoMigration, DIContainer
Presentation/ImportController (upload, preview, jobs, stats) e LookupController (CRUD tabelle e record), RouteRegistration

Pattern adottati: Facade (ImportService su ImportOrchestrator + ImportJobService), Repository con port-and-adapter sul layer dominio, Chain of Responsibility per le trasformazioni, elaborazione asincrona del job in background con aggiornamento incrementale dello stato, schema importexport autocreato e versionato tramite file 001005 in migrations/.

Autenticazione: il servizio si affida a nginx (auth_requestnode-user-auth) e non implementa identity in proprio. Il rate limit pubblico (createRateLimitConfig.forPublicApi, finestra di 15 minuti) escludelusivamente /health e /ready. CORS è guidato da CORS_ORIGINS (whitelist).

Casi d'uso

  • Caricamento anagrafiche batch: l'operatore carica un .xlsx di clienti/fornitori; il frontend chiama POST /import/preview per mostrare le prime righe e configurare il mapping colonne, poi POST /import/upload con columnMappings e enableTransformation: true per normalizzare maiuscole, codice fiscale e date
  • Aggiornamento massivo via External ID: il foglio contiene una colonna codiceesterno che mappa a record già esistenti — l'orchestrator costruisce in batch la corrispondenza External ID → ID interno e produce un mix INSERT/UPDATE invece di duplicare le righe
  • Normalizzazione tramite lookup: una colonna "tipo pagamento" testuale viene risolta contro una lookup table (POST /lookup/tables/{name}/lookup/{key}) per ottenere il codice interno; eventuali valori non noti vengono individuati via fuzzy match e segnalati nei warning del job
  • Arricchimento da provider esterno: per ogni riga il DataEnrichmentService chiama un endpoint HTTP configurato (es. validazione partita IVA, geocoding indirizzi) a batch, con pausa configurabile tra i batch per rispettare i rate limit del provider
  • Monitoraggio import lunghi: il frontend polla GET /import/jobs/{jobId} per mostrare la progress bar (processedRows/totalRows) e gli aggregati finali (rowsCreated/rowsUpdated/rowsFailed) a fine job

Identità & esposizione

CampoValore
Categoriadata-io
Versione cluster1.0.6
Imagegitea.pzetatouch.it/pzeta_touch/node-excel-import:1.0.6
URL pubblicohttps://ditta.pzeta.it/excel-import
Path regex ingress`/excel-import(/
Rewrite a backend/$2
DNS internonode-excel-import-ditta.ditta.svc.cluster.local:3000
Auth nginxauth_requestnode-user-auth
Repositorynode-excel-import
Endpoint REST13 (vedi sezione "API reference")

Endpoint operazionali

Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:

Path pubblicoScopo
https://ditta.pzeta.it/excel-import/healthliveness probe
https://ditta.pzeta.it/excel-import/readyreadiness probe
https://ditta.pzeta.it/excel-import/metricsmetriche Prometheus
https://ditta.pzeta.it/excel-import/api-docs.jsonspec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true)
https://ditta.pzeta.it/excel-import/api-docsSwagger UI (solo in NODE_ENV !== production)

Configurazione

Variabili d'ambiente rilevanti per chi integra il servizio (per la lista completa vedi .env.default del repo):

VariabileRuolo
HOST / PORTBind del server (0.0.0.0:3000 in container, 127.0.0.1:3000 di default)
NODE_ENVproduction, development o test; influisce su esposizione Swagger UI e formattazione log
DATABASE_HOST / _PORT / _NAME / _USER / _PASSWORDConnessione PostgreSQL — job, lookup table, risorse importate, metadati, External ID
DATABASE_POOL_MAXDimensione massima del pool pg (default 20)
AUTO_MIGRATION_ENABLEDSe false salta l'auto-migration al bootstrap; di default abilitato
ENABLE_CORS / CORS_ORIGINSAttiva CORS e definisce la whitelist degli origin consentiti (separati da virgola)
REVERSE_PROXYSe valorizzato, allunga keepAliveTimeout/headersTimeout per la convivenza con nginx
OPENAPI_EXPOSE_IN_PRODUCTIONEspone /api-docs.json anche in produzione; Swagger UI resta off in produzione
ENRICHMENT_BATCH_PAUSE_MSPausa fra batch di enrichment HTTP (default 100ms, minimo 0)
APP_URLURL pubblico dell'applicazione, usato in alcuni link applicativi

Lo schema PostgreSQL importexport viene creato e versionato automaticamente al bootstrap dai file in migrations/ (sequenza 001005); l'esecuzione è protetta da pg_advisory_lock per consentire l'avvio simultaneo di più repliche.

Note eventing NATS

Il servizio è attualmente HTTP-only: non pubblica né consuma subject NATS, non si registra come modulo presso node-orchestrator e non integra direttamente node-postgrest-sidecar. La persistenza delle righe importate avviene tramite un repository PostgreSQL generico interno che scrive nello schema target via driver pg. L'integrazione con orchestrator/eventing è prevista come evoluzione (vedi issue upstream) ma non è ancora implementata, quindi nats.json risulta correttamente vuoto.

Dipendenze e dipendenti

Dipende da (servizi che questo servizio chiama):

Consumato da (chi chiama questo servizio):

  • frontend Vue

Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.

Loading OpenAPI…
Loading NATS contracts…