Evit@v2 – API

L’architettura backend si basa su un sistema di middleware che elaborano ogni richiesta prima che questa raggiunga i controller. Questo approccio permette di separare la gestione dei dati dalle singole funzionalità dell’applicazione.

Ogni chiamata attraversa un percorso organizzato che gestisce sicurezza, logging, lingua e validazione degli input. In particolare, il sistema si occupa di autenticare l’utente tramite JWT, tradurre i messaggi con i18n, tracciare le prestazioni con Winston e velocizzare le risposte usando Redis. Infine, la validazione con Joi blocca i dati errati e invia al frontend messaggi di errore già formattati e pronti da mostrare.

Middleware

Il backend in Node.js sfrutta la logica dei middleware integrati nel router delle API per gestire la sicurezza, il contesto utente, il logging e la localizzazione.

Autenticazione

Nello specifico, il middleware di autenticazione intercetta ogni richiesta protetta e segue questo flusso:

  1. Verifica del Token: Controlla la presenza e la validità del token JWT nell’header della richiesta HTTP.
  2. Gestione Errori (HTTP 401): Se il token manca o non è valido, il server restituisce il codice 401 Unauthorized. Questo segnale forza il client a avviare la procedura di refresh token per ottenere un nuovo JWT e ripristinare la sessione in modo trasparente.
  3. Iniezione del Profilo Utente: Se il token è valido, il middleware recupera dal database i dati dell’utente e la relativa immagine avatar.
  4. Ottimizzazione Avatar: L’avatar viene incluso nel profilo utente iniettato nella richiesta solo se le sue dimensioni non superano il limite di 300 KB.

Grazie a questo approccio, qualsiasi endpoint protetto viene validato automaticamente e ha subito a disposizione i dati del profilo dell’utente autenticato all’interno dell’oggetto della richiesta.

Localizzazione

Questo middleware determina la lingua dell’utente controllando i parametri URL o l’header HTTP (con fallback in inglese) e la imposta nell’ambiente di internazionalizzazione i18n e nelle variabili locali della risposta per le fasi successive.

Nello specifico, il suo funzionamento si articola in tre passaggi:

  • Individuazione della lingua: Verifica prima se la lingua è stata specificata direttamente nell’indirizzo web (es. ?lang=it). Se non la trova, controlla le impostazioni inviate dal browser dell’utente (l’header Accept-Language). Se anche questo dato manca, assegna automaticamente l’inglese come lingua predefinita.
  • Pulizia del valore: Poiché i browser inviano spesso elenchi complessi di lingue con varie preferenze, il codice isola ed estrae soltanto la prima lingua indicata.
  • Salvataggio nel sistema: Applica la lingua scelta alla libreria di traduzione (i18n) e la rende disponibile per tutto il resto dell’applicazione, così che le risposte e le pagine successive possano essere mostrate nella lingua corretta.

Logging

Il middleware di logging si occupa di intercettare le richieste HTTP memorizzando il metodo, l’URL e l’ora d’inizio, per poi calcolare il tempo di risposta totale non appena il server invia i dati al client.
Infine, registra tramite la libreria Winston un messaggio di log differenziato in base allo status code restituito per distinguere successi, avvisi ed errori di sistema, seguendo questo schema:

  • Status > 500 (Errori Server): logga con logger.error (es. crash o bug interni).
  • Status 400 – 499 (Errori Client): logga con logger.warn (es. 404 Not Found, 401 Unauthorized).
  • Status < 400 (Successi e Reindirizzamenti): logga con logger.info (es. 200 OK, 304 Not Modified).

winston: È la libreria di logging più famosa per Node.js. Serve a registrare eventi, messaggi di info, warning ed errori (invece di usare il semplice console.log). Gestisce livelli di severità, formattazione JSON o testo e l’invio dei dati a diverse destinazioni (console, file, database, servizi cloud).

winston-daily-rotate-file: È un modulo aggiuntivo (transport) per Winston che salva i log su file fisici e li ruota automaticamente su base giornaliera o per dimensione massima (es. creando file tipo application-2026-09-05.log).

Redis

Il sistema impiega Redis per ottimizzare la gestione delle API che restituiscono dati statici o a bassa frequenza di aggiornamento, come province, categorie e risorse.

L’integrazione segue questa struttura e flussi logici:

Vantaggi dell’Architettura

  • Latenza quasi zero: risposte istantanee grazie alla gestione dati in-memory.
  • Offload del DB: elimina query ridondanti al database principale.
  • Scalabilità orizzontale: gestisce picchi di traffico elevati senza degradare le prestazioni.
  • Flessibilità dei dati: supporta strutture complesse (stringhe, hash, liste, set) direttamente in cache.
  • Persistenza opzionale: unisce la velocità della RAM alla sicurezza del salvataggio su disco.

Flusso della Funzione cacheResponse

  • Verifica disponibilità Redis: Se il servizio Redis non è attivo, la funzione salta il layer di caching e interroga direttamente il database.
  • Lettura dalla Cache: Se Redis è attivo, verifica la presenza del dato associato alla chiave (es. resource:guid=${guid}). Se il dato è presente, viene restituito all’istante.
  • Esecuzione della Query (Cache Miss): Se il dato non è in cache, esegue la funzione di fallback (la query Prisma findUnique).
  • Salvataggio Condizionato e TTL: Se il risultato ottenuto non supera la dimensione massima consentita, viene salvato su Redis con un tempo di vita prestabilito (TTL, ad esempio 1 ora) prima di essere restituito; in caso contrario, il dato viene semplicemente restituito senza occupare memoria in cache.

JavaScript

const resource = await cacheResponse(`resource:guid=${guid}`, async() => {
    return await prisma.resources.findUnique({
        where: { guid: guid },
    });
});

Schemi di validazione

Ogni chiamata a un’API passa dal routing, che si occupa di applicare uno schema di validazione tramite la libreria Joi prima dell’esecuzione del controller. Questo processo garantisce, ad esempio, che i parametri richiesti siano presenti e che il formato di alcuni campi (come l’email) rispetti specifiche regole (Regex).

Esempio di applicazione del middleware di validazione su una rotta API:

router.post('/login', validateBody(loginSchema), authController.login);

Per permettere al frontend di mappare facilmente gli errori sui singoli campi del form (es. evidenziare l’input rosso e mostrare il messaggio sotto il campo), la struttura generata da questo middleware restituisce una risposta JSON con stato 400 Bad Request organizzata così:

{
  "errors": [
    {
      "name": "email",
      "message": "L'indirizzo email non è valido"
    },
    {
      "name": "user.name",
      "message": "Il nome è obbligatorio"
    }
  ]
}

I messaggi d’errore vengono tradotti dal middleware di localizzazione precedentemente descritto, sollevando il frontend da qualsiasi logica di traduzione e permettendogli di visualizzarli direttamente.

Joi: libreria di riferimento per la validazione dei dati in Node.js. Permette di definire uno schema (un insieme di regole) per verificare che le informazioni ricevute dal backend — come il corpo di una richiesta HTTP, i parametri o le query — siano corrette, sicure e ben formattate prima di essere elaborate dall’applicazione.