node-excel-export

Esportazione di dati in formato Excel/CSV.
data-io
/excel-exportauth: nginx

node-excel-export

In sintesi

Servizio di esportazione tabellare che trasforma payload JSON o query su PostgreSQL in file Excel (XLSX) e CSV pronti al download. Espone due workflow distinti: uno legacy a payload diretto (il chiamante passa righe e intestazioni) e uno dinamico basato su modelli persistiti su DB, dove il client indica risorsa, campi, filtri e ordinamento, e il servizio costruisce la query, esegue l'estrazione paginata e formatta il file. È il punto di uscita standard per le funzioni "Esporta" del frontend Vue del tenant ditta.

Oltre al download tradizionale, il servizio supporta l'anteprima inline tramite il parametro query disposition=inline sugli endpoint di export, abilitando l'embedding del file in iframe/object lato frontend. Si registra inoltre come modulo orchestrator (@pzeta/orchestrator-node) esponendo il task sincrono export-dataset per essere richiamato da workflow inter-servizio.

Funzionalità principali

  • Export sincrono da payload JSON (POST /export) per fogli singoli o multi-worksheet con intestazioni, righe e merge cells dichiarate dal client
  • Export dinamico da template DB (POST /api/export/dynamic) che esegue query sicure su risorse registrate (anagrafica, articoli, ecc.) applicando campi, filtri, ordinamento e limite
  • Content-Disposition selezionabile via query param ?disposition=inline|attachment (default attachment) sugli endpoint di export legacy e dinamico: inline abilita la preview embedded del file in iframe/object/viewer del browser senza forzare il download
  • Registrazione come modulo orchestrator (@pzeta/orchestrator-node) con task sincrono export-dataset che replica il workflow dinamico via NATS request/reply e restituisce il file in base64; attiva solo se ORCHESTRATOR_ENABLED=true (in caso contrario il servizio opera in modalità degraded solo HTTP)
  • CRUD modelli di export persistiti su PostgreSQL (modelliexport): creazione, aggiornamento, soft delete e contatore volteusato per le statistiche
  • Discovery metadati campi per risorsa (GET /api/export/metadata/:risorsa/fields) consumata dalla libreria @pzeta/vue-importexport per costruire l'UI di selezione
  • Strategy multi-formato (XLSX via exceljs, CSV streaming) selezionata dinamicamente dal formato richiesto
  • Rate limiting per IP configurabile (default 10 richieste / 60 s) sulla rotta di export, applicato dal securityPlugin di @pzeta/fastify-utils
  • Validazione strutturale Zod su request, schema OpenAPI auto-generato e formattazione uniforme degli errori HTTP 400/422
  • Logging applicativo delle esportazioni su tabella logexportazioni per audit e diagnostica, con request ID di tracing propagato
  • Migrazioni automatiche dello schema importexport all'avvio (idempotenti) se RUN_MIGRATIONS=true

Architettura

Stack: Fastify v5 · TypeScript strict · @pzeta/fastify-utils (security/error-handler/healthcheck/openapi/metrics/tracing/logging) · @pzeta/log per logging strutturato · @pzeta/orchestrator-node per la registrazione come modulo orchestrator e l'esposizione di task via NATS · PostgreSQL via driver pg diretto · exceljs per la generazione XLSX · Zod per la validazione · DI container interno (infrastructure/container/DIContainer.ts) con risoluzione per token string.

Layout DDD (src/):

LayerContenuto
domain/Entità (ExportJob, ExportTemplate, ExportLog, Workbook, Worksheet), repository interface (IExportTemplateRepository, IExportLogRepository, IFileManager, IDatabaseQueryProvider), servizi di dominio per formattazione ed export
application/DTO request/response, ExportApplicationService, DynamicExportService, DynamicQueryService, RequestValidationService, adapter verso strategie infrastrutturali
infrastructure/PostgresConnection (pool pg), MigrationRunner, repository concreti, ExportStrategyFactory con XlsxExportStrategy e CsvExportStrategy, EnvironmentConfig singleton, orchestrator/ (OrchestratorAdapter, OrchestratorConfigurationService con strategia soft-fail/degraded mode, handler ExportDatasetHandler e schemi Zod dei task)
presentation/Plugin di route (export.routes, dynamicExport.routes, exportLimits.routes, exportExamples.routes, buildInfo.routes), controller dedicati per ciascuna famiglia di endpoint, schemi Fastify+JSON-Schema generati da Zod (incluso ExportQuerySchema per il parametro disposition)

Pattern adottati: Strategy (un'implementazione per ogni formato di output), Factory per la selezione della strategia, Repository per la persistenza dei template e dei log, DI container leggero con resolve per token. La generazione del file avviene in memoria (stream) e ritorna Content-Disposition: attachment direttamente nella response, senza scrittura su disco né caricamento su storage esterno.

Sicurezza: dietro nginx con auth_request verso node-user-auth (strategia nginx); nessun token applicativo verificato dal servizio. Il rate limiting è applicato per IP a livello di plugin.

Casi d'uso

  • Export "Esporta tabella" dal frontend: una griglia Vue del Laundry ERP (anagrafiche, articoli, ordini) invoca POST /api/export/execute (alias compatibile con @pzeta/vue-importexport) con risorsa, campi selezionati, filtri della ricerca e ordinamento corrente; riceve il file XLSX da scaricare
  • Anteprima inline del file generato: il frontend richiede POST /api/export/dynamic?disposition=inline per ottenere un XLSX/CSV renderizzato direttamente in un viewer embedded (iframe/object) senza forzare il dialog di download — utile per preview di report e pannelli "guarda prima di scaricare"
  • Riuso di un modello salvato: l'operatore seleziona un template precedente (GET /api/export/templates/:risorsa), il servizio lo applica con i filtri runtime e incrementa volteusato per le statistiche d'uso
  • Export ad-hoc da modulo legacy: un microservizio chiama POST /export passando direttamente intestazioni e righe già pronte (ad esempio un report aggregato calcolato altrove), senza coinvolgere la pipeline dinamica
  • Workflow orchestrator con task export-dataset: un workflow definito su node-orchestrator invoca il task export-dataset di questo modulo (via NATS request/reply) per generare un export inline e passarlo come step successivo — ad esempio come allegato di una notifica inviata da node-notification o come input per una pipeline di archiviazione
  • Step di un workflow orchestrato: node-orchestrator esegue un export programmato come task di un workflow notturno e inoltra l'output a node-storage o a un destinatario via node-notification
  • Discovery UI: la libreria @pzeta/vue-importexport chiama GET /api/export/metadata/:risorsa/fields per popolare il selettore campi con i metadati ufficiali della risorsa (label, tipo, gruppo)

Identità & esposizione

CampoValore
Categoriadata-io
Versione cluster1.0.3
Imagegitea.pzetatouch.it/pzeta_touch/node-excel-export:1.0.3
URL pubblicohttps://ditta.pzeta.it/excel-export
Path regex ingress`/excel-export(/
Rewrite a backend/$2
DNS internonode-excel-export-ditta.ditta.svc.cluster.local:3000
Auth nginxauth_requestnode-user-auth
Repositorynode-excel-export
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-export/healthliveness probe
https://ditta.pzeta.it/excel-export/readyreadiness probe
https://ditta.pzeta.it/excel-export/metricsmetriche Prometheus
https://ditta.pzeta.it/excel-export/api-docs.jsonspec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true)
https://ditta.pzeta.it/excel-export/api-docsSwagger UI (solo in NODE_ENV !== production)

Configurazione

Variabili d'ambiente che un integratore deve conoscere (per la lista completa vedi .env.example del repo):

VariabileRuolo
DATABASE_HOST / _PORT / _USER / _PASSWORD / _NAMEConnessione PostgreSQL; lo schema applicativo è importexport (template, log) ma il servizio interroga anche le tabelle business per l'export dinamico
DATABASE_POOL_MAX / _IDLE_TIMEOUT_MS / _CONNECTION_TIMEOUT_MSTuning del pool pg; default 20 / 30000 / 2000 ms
RUN_MIGRATIONSEsegue le migrazioni dello schema importexport all'avvio; true di default
TEMP_DIRECTORYCartella per file temporanei di lavoro (./temp); il servizio non persiste l'output, ma può usarla per buffering
MAX_FILE_SIZE_MBLimite hard sulla dimensione del file generato (default 50 MB); oltre il limite la richiesta fallisce con 413
EXPORT_RATE_LIMIT_REQUESTS / EXPORT_RATE_LIMIT_WINDOW_MSRate limit per IP sulle rotte di export (default 10 richieste / 60 s)
CORS_ALLOWED_ORIGINSOrigini consentite in produzione, separate da virgola; in NODE_ENV !== production la policy è aperta
REVERSE_PROXYAbilita trustProxy di Fastify quando il servizio è dietro nginx
ORCHESTRATOR_ENABLEDAbilita la registrazione come modulo orchestrator e l'esposizione del task export-dataset via NATS; default false. Se la connessione fallisce il servizio prosegue in modalità degraded HTTP-only
ORCHESTRATOR_URLURL dell'orchestrator a cui registrarsi (obbligatorio quando ORCHESTRATOR_ENABLED=true, validato come URL)
ORCHESTRATOR_MODULE_IDID univoco del modulo nel registry orchestrator; default node-excel-export
ORCHESTRATOR_API_KEYAPI key opzionale per l'autenticazione del modulo verso l'orchestrator
MODULE_VERSION / MODULE_NAME / MODULE_DESCRIPTIONOverride opzionali del manifest del modulo (default letti da package.json)

Swagger UI è esposto solo se NODE_ENV !== production; le metriche Prometheus sono pubblicate con prefisso excel_export_.

Note eventing NATS

Il servizio partecipa al bus NATS solo in modalità request/reply come modulo orchestrator, non come publisher di eventi di dominio. Quando ORCHESTRATOR_ENABLED=true l'OrchestratorAdapter istanzia un ModuleSDK (@pzeta/orchestrator-node), costruisce il manifest del modulo a partire da package.json (override possibile via MODULE_VERSION/MODULE_NAME/MODULE_DESCRIPTION) e registra l'unico task attualmente supportato:

  • export-dataset — task sincrono che riceve gli stessi parametri di POST /api/export/dynamic (risorsa, fileName, formato, templateId o campi inline, filtri, opzioni), esegue l'export tramite DynamicExportService, legge il file generato e lo restituisce nella response NATS serializzato in base64 insieme a fileName, format, byteSize, rowCount, processingTimeMs. Il file temporaneo viene rimosso dopo la lettura (best-effort).

I subject NATS effettivi (tasks.<moduleId>.export-dataset e analoghi) non sono hard-coded nel codice: vengono derivati a runtime dal manifest del modulo dall'SDK orchestrator. Per questo motivo lo scanner statico continua a rilevare zero publish e zero subscribe in nats.json — l'informazione è disponibile solo dopo la registrazione live presso l'orchestrator (visibile su getRegisteredTasks() e tramite gli endpoint del registry orchestrator).

Cosa NON è ancora implementato: il task asincrono export-dataset-async (con persistenza del file su node-storage e ack immediato) e la pubblicazione di eventi di completamento sul subject services.excel-export.job.completed (e relativi job.started/job.failed) sono dichiarati esplicitamente fuori scope in questa iterazione (vedi commento di estensione in ExportDatasetHandler.ts e OrchestratorTaskSchemas.ts, issue PZeta_Touch/node-excel-export#12 risolta solo parzialmente). Eventuali audit cross-servizio sulle esportazioni continuano quindi a basarsi sulla tabella logexportazioni interna, non su eventi di dominio NATS.

In modalità degraded (orchestrator disabilitato o irraggiungibile) il servizio continua a funzionare esclusivamente come microservizio HTTP REST: i client (frontend Vue, altri microservizi) effettuano una POST e ricevono lo stream del file, con disposition attachment o inline a seconda del query param.

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…