JavaScript

Creare un backend con Node.js, Express e MongoDB

Creare un backend con Node.js, Express e MongoDB
LT
Luca Terribili
Autore
📚 8 capitoli

Quando si sviluppa un'applicazione web, una delle prime decisioni da prendere riguarda la struttura del progetto. Un'interfaccia moderna, realizzata con Angular, React o Svelte, può occuparsi della presentazione dei dati e delle interazioni con l'utente, ma prima o poi deve comunicare con un sistema che gestisca le informazioni, applichi le regole applicative e interagisca con un database.

È qui che entra in gioco il backend. Si tratta della parte dell'applicazione che lavora sul server e che normalmente espone delle API attraverso cui il frontend può leggere, inserire, aggiornare o eliminare dati. Il backend può inoltre occuparsi dell'autenticazione, dei permessi, della validazione delle richieste e di tutte quelle operazioni che non dovrebbero essere affidate esclusivamente al browser.

In questa serie costruirò un backend utilizzando Node.js, Express e MongoDB. Non mi limiterò a creare un server che risponde a una richiesta HTTP: l'obiettivo è arrivare progressivamente a un'applicazione strutturata, capace di esporre API REST, salvare dati persistenti e gestire correttamente gli errori. Userò TypeScript per rendere il codice più robusto e per introdurre fin dall'inizio un sistema di tipi utile anche nei progetti di dimensioni maggiori.

Il percorso seguirà un approccio pratico. Ogni componente verrà introdotto quando servirà davvero, evitando di accumulare configurazioni e concetti che non abbiamo ancora avuto modo di utilizzare. Le scelte tecniche saranno accompagnate da spiegazioni e verifiche concrete, perché un tutorial utile non dovrebbe limitarsi a mostrare codice da copiare, ma aiutare a capire perché quel codice esiste e quali problemi risolve.

Che cosa costruiremo

Il progetto finale sarà un backend REST che gestisce una raccolta di risorse attraverso operazioni HTTP. Per rendere gli esempi semplici da comprendere, potremo utilizzare una risorsa come i prodotti di un catalogo. Ogni prodotto avrà alcuni campi, per esempio un identificativo, un nome, una descrizione e un prezzo.

Il backend permetterà di recuperare l'elenco dei prodotti, ottenere il dettaglio di un singolo elemento, crearne uno nuovo, modificarlo ed eliminarlo. Queste operazioni rappresentano le classiche funzionalità CRUD, acronimo di Create, Read, Update, Delete.

I dati non saranno conservati soltanto nella memoria del processo Node.js. Utilizzeremo MongoDB, un database documentale che permette di memorizzare oggetti strutturati in documenti BSON, un formato binario compatibile con JSON ma dotato di tipi aggiuntivi. Questo ci consentirà di mantenere le informazioni anche dopo il riavvio del server.

Il progetto verrà sviluppato per passi successivi. Prima prepareremo l'ambiente, poi realizzeremo il server HTTP, introdurremo TypeScript, collegheremo Express e infine integreremo MongoDB. In seguito affronteremo la validazione degli input, la gestione degli errori, le variabili d'ambiente, i test e le basi necessarie per portare il backend in produzione.

Perché scegliere Node.js

Node.js è un runtime JavaScript che permette di eseguire codice JavaScript al di fuori del browser. È basato sul motore JavaScript V8, lo stesso utilizzato da Google Chrome, e offre API per lavorare con file, rete, processi e altre risorse del sistema.

Questo significa che possiamo utilizzare JavaScript non soltanto per sviluppare l'interfaccia di un'applicazione, ma anche per implementare la logica del server. Se il frontend utilizza già JavaScript o TypeScript, adottare gli stessi linguaggi anche sul backend permette di condividere alcune competenze e, quando opportuno, tipi e strutture dati tra i diversi livelli dell'applicazione.

Uno degli aspetti più interessanti di Node.js è il modello di esecuzione orientato alle operazioni asincrone. Le richieste di rete e le operazioni di input/output possono essere gestite senza bloccare continuamente il flusso principale di esecuzione in attesa che ogni operazione termini. Questo modello è particolarmente adatto a molti backend che devono gestire numerose richieste, consultare database e chiamare servizi esterni.

Non significa, però, che Node.js renda automaticamente veloce qualsiasi applicazione. Le operazioni intensive sulla CPU, il codice inefficiente e le query mal progettate possono comunque diventare colli di bottiglia. Come sempre, la tecnologia è uno strumento: il risultato dipende anche da come viene utilizzata.

Il ruolo di Express

Node.js mette a disposizione le API necessarie per creare un server HTTP, ma gestire manualmente ogni aspetto delle richieste diventerebbe rapidamente scomodo. Dovremmo occuparci direttamente del routing, dell'interpretazione dei parametri, della gestione degli errori e di molti dettagli ripetitivi.

Express è un framework web leggero che semplifica questo lavoro. Permette di definire le rotte dell'applicazione, associare funzioni alle richieste HTTP e organizzare la gestione delle richieste e delle risposte attraverso un sistema di middleware.

Per esempio, potremo definire una rotta GET /api/products che restituisce l'elenco dei prodotti e una rotta POST /api/products che permette di crearne uno. Express si occuperà di indirizzare la richiesta alla funzione corretta, mentre il nostro codice gestirà la logica applicativa e l'accesso al database.

Express non è un database e non sostituisce Node.js. È un livello aggiuntivo che ci permette di organizzare il server in modo più chiaro. Questa distinzione è importante: comprendere il ruolo di ogni componente rende più semplice individuare un problema quando qualcosa non funziona.

Perché utilizzare TypeScript

JavaScript è un linguaggio dinamico: generalmente non richiede di dichiarare in anticipo il tipo di ogni variabile. Questa flessibilità è utile, ma nei progetti più articolati può rendere alcuni errori difficili da individuare prima dell'esecuzione.

TypeScript aggiunge un sistema di tipi a JavaScript. Possiamo definire che un prezzo è un numero, che il nome di un prodotto è una stringa oppure che una funzione deve restituire una determinata struttura di dati. Il compilatore può quindi segnalare molte incongruenze prima che il programma venga eseguito.

TypeScript non viene normalmente eseguito direttamente da Node.js come se fosse JavaScript. Il codice deve essere trasformato in una forma eseguibile oppure gestito da uno strumento compatibile con l'ambiente di sviluppo. Nel nostro progetto useremo strumenti distinti per eseguire rapidamente il codice durante lo sviluppo e per controllare i tipi e generare l'output destinato alla produzione.

È importante chiarire anche un limite: i tipi TypeScript non validano automaticamente i dati ricevuti da una richiesta HTTP. Un client può inviare dati non validi anche quando il nostro codice dichiara un'interfaccia perfettamente definita. Per questo motivo introdurremo successivamente una validazione esplicita degli input.

Perché utilizzare MongoDB

MongoDB è un database documentale. A differenza di un database relazionale tradizionale, che organizza normalmente i dati in tabelle e righe, MongoDB memorizza documenti raggruppati in collezioni.

Un documento può rappresentare, per esempio, un prodotto:

{
  "name": "Tastiera meccanica",
  "description": "Tastiera con layout italiano",
  "price": 89.9
}

Il documento contiene campi e valori che descrivono l'oggetto. MongoDB permette di effettuare ricerche, inserimenti, aggiornamenti ed eliminazioni sui documenti e mette a disposizione strumenti per gestire gli indici e le relazioni tra informazioni quando necessario.

La flessibilità del modello documentale non significa che sia opportuno salvare dati senza progettazione. È comunque necessario stabilire quali campi sono obbligatori, quali tipi di dati sono ammessi, come vengono identificati i documenti e quali query deve supportare l'applicazione.

Per collegare Node.js a MongoDB utilizzeremo inizialmente il driver ufficiale, così da comprendere il funzionamento del database senza introdurre subito un ulteriore livello di astrazione. Una volta chiari i concetti fondamentali, sarà più semplice valutare anche librerie di livello superiore.

Come funzionerà l'architettura

L'applicazione seguirà una struttura client-server. Il frontend invierà una richiesta HTTP al backend; Express individuerà la rotta corrispondente; il codice applicativo elaborerà la richiesta e, quando necessario, interrogherà MongoDB. Infine, il server restituirà una risposta HTTP, normalmente contenente dati JSON.

Il flusso sarà quindi:

Frontend → richiesta HTTP → Node.js ed Express → MongoDB → risposta JSON

Questa separazione offre diversi vantaggi. Il frontend non deve conoscere le credenziali del database e non accede direttamente ai dati persistenti. Le regole applicative vengono concentrate sul server e possono essere applicate indipendentemente dall'interfaccia utilizzata. In futuro, lo stesso backend potrà essere consumato da un'applicazione Svelte, da un progetto Angular o da un altro client capace di effettuare richieste HTTP.

L'API costituirà quindi il punto di comunicazione tra frontend e backend. Progetteremo gli endpoint in modo coerente, utilizzando i metodi HTTP appropriati e restituendo codici di stato che permettano al client di distinguere una richiesta riuscita da un errore.

Prerequisiti

Per seguire il tutorial è utile conoscere le basi di JavaScript, in particolare variabili, funzioni, oggetti, moduli e funzioni asincrone. Non è necessario conoscere già Express o MongoDB: li introdurremo progressivamente.

Servono inoltre Node.js e npm, il gestore dei pacchetti normalmente distribuito insieme a Node.js. Utilizzeremo npm per installare le dipendenze del progetto e per definire i comandi che ci permetteranno di avviare il server ed eseguire i controlli.

È consigliabile lavorare in un ambiente Linux, macOS o Windows con un terminale disponibile e un editor di codice. Per il database potremo scegliere tra un'installazione locale di MongoDB e un'istanza remota, per esempio tramite un servizio cloud compatibile. La scelta verrà affrontata quando arriveremo all'integrazione, così da non introdurre configurazioni non ancora necessarie.

Prima di iniziare, verifichiamo che Node.js e npm siano disponibili. Dal terminale eseguiamo:

node --version
npm --version

Il primo comando mostra la versione di Node.js installata, mentre il secondo mostra quella di npm. Se entrambi restituiscono un numero di versione, possiamo proseguire con la preparazione del progetto.

Conclusione

In questa prima lezione abbiamo definito l'obiettivo del percorso e il ruolo dei componenti che utilizzeremo. Node.js fornirà l'ambiente di esecuzione, Express semplificherà la gestione delle richieste HTTP, TypeScript ci aiuterà a controllare la struttura del codice e MongoDB conserverà i dati dell'applicazione.

Non abbiamo ancora realizzato il backend vero e proprio: abbiamo stabilito l'architettura e chiarito perché ogni tecnologia è presente nel progetto. È un passaggio utile perché, quando inizieremo a scrivere codice, potremo concentrarci su un problema alla volta senza confondere il runtime, il framework web e il database.

Nella prossima lezione prepareremo l'ambiente di sviluppo, creeremo la directory del progetto e analizzeremo i primi comandi npm. Da lì inizieremo a costruire concretamente il backend, verificando ogni passaggio prima di aggiungere nuove funzionalità.

📚 Capitoli del Tutorial

Per sviluppare un backend con Node.js, Express e MongoDB è necessario preparare correttamente l'ambiente di sviluppo. Node.js permette di eseguire JavaScript sul server, mentre npm consente di installare le librerie necessarie e gestire le dipendenze del progetto.

Nel precedente tutorial ho preparato l'ambiente di sviluppo per il mio progetto backend, installando Node.js, npm, Express e gli strumenti necessari per lavorare con TypeScript. Adesso è arrivato il momento di configurare il compilatore e capire come utilizzare TypeScript all'interno di un progetto Node.js.

Nei tutorial precedenti ho installato Node.js e npm, preparato l'ambiente di sviluppo e configurato TypeScript. Ho anche verificato che il compilatore funzioni correttamente e che sia possibile eseguire file TypeScript utilizzando tsx.

Nel tutorial precedente ho creato il primo server Express utilizzando Node.js e TypeScript. Il server è in grado di avviarsi, ricevere richieste HTTP e restituire una risposta JSON. È una base funzionante, ma il codice è ancora concentrato in un unico file.

Nei tutorial precedenti ho creato un backend con Node.js, Express e TypeScript. Ho separato le rotte dalla configurazione dell'applicazione e dal punto di ingresso del server, ottenendo una struttura più semplice da mantenere.

Nei tutorial precedenti abbiamo preparato l'ambiente di sviluppo, configurato Node.js con TypeScript, organizzato il backend Express e predisposto la connessione a MongoDB. Abbiamo anche deciso di eseguire il database in un container Docker, così da avere un ambiente locale indipendente da servizi esterni.

Dopo aver creato un backend con Express e TypeScript e aver implementato le operazioni CRUD con MongoDB, il passo successivo è permettere agli utenti di identificarsi. Finora, infatti, le API dei prodotti non distinguono una richiesta proveniente da un utente registrato da una proveniente da chiunque altro.

Nel tutorial precedente ho implementato la registrazione e il login degli utenti. Dopo aver verificato le credenziali, il backend genera un JSON Web Token che il client può utilizzare nelle richieste successive.