Introduzione a Express.js
Se hai già fatto i primi passi con Node.js, saprai che creare un server web utilizzando esclusivamente il modulo nativo http può diventare rapidamente complesso e ripetitivo. È qui che entra in gioco Express.js.
Express è un framework web per Node.js, minimale e flessibile, che fornisce un set di robusti strumenti per gestire server e applicazioni web. In termini semplici, Express agisce come uno strato di astrazione sopra Node.js, semplificando drasticamente l'operazione di gestione delle rotte (routing), l'integrazione di middleware e la gestione delle richieste HTTP.
Perché usare Express invece di Node.js puro?
Immagina di dover gestire dieci diverse pagine sul tuo sito. Con Node.js puro, dovresti scrivere una serie di istruzioni if o switch basandoti sull'URL della richiesta (req.url). Con Express, puoi definire queste rotte in modo dichiarativo, rendendo il codice più leggibile, manutenibile e professionale.
Express è diventato lo standard de facto per lo sviluppo backend con JavaScript, formando insieme a MongoDB, Express, React e Node l'ecosistema noto come MERN Stack.
Installazione e Configurazione Iniziale
Prima di iniziare, assicurati di avere Node.js installato sul tuo computer. Puoi verificarlo aprendo il terminale e digitando node -v.
Passaggi per l'installazione
- Crea una cartella per il progetto: Apri il terminale e crea una nuova directory.
- Inizializza il progetto: Esegui
npm init -yper creare il filepackage.json, che conterrà tutte le informazioni e le dipendenze del tuo progetto. - Installa Express: Esegui il comando
npm install express.
Ora che l'ambiente è pronto, creiamo il nostro primo file, ad esempio app.js.
// Importiamo il modulo express
const express = require('express');
// Inizializziamo l'applicazione Express
const app = express();
// Definiamo la porta su cui il server ascolterà
const PORT = 3000;
// Creiamo una rotta di base (Home Page)
app.get('/', (req, res) => {
res.send('Benvenuti nel mio primo server Express!');
});
// Avviamo il server
app.listen(PORT, () => {
console.log(`Server in ascolto su http://localhost:${PORT}`);
});
Spiegazione del codice:
require('express'): Importa la libreria all'interno del file.app.get('/', ...): Definisce un "endpoint". Quando l'utente visita la radice del sito (/), la funzione di callback viene eseguita, inviando una risposta testuale tramiteres.send().app.listen(): Avvia effettivamente il processo del server, rendendolo capace di ricevere richieste in entrata.
Approfondimento sul Routing
Il routing è il cuore di ogni applicazione web. Si riferisce al modo in cui l'applicazione risponde a una richiesta client per un endpoint specifico.
Metodi HTTP
Express supporta tutti i metodi HTTP standard, ma i più comuni sono:
- GET: Utilizzato per richiedere dati dal server (es. leggere un articolo).
- POST: Utilizzato per inviare nuovi dati al server (es. creare un nuovo utente).
- PUT/PATCH: Utilizzati per aggiornare dati esistenti.
- DELETE: Utilizzato per rimuovere dati.
Rotte Dinamiche
Spesso non conosciamo l'URL esatto che l'utente richiederà (ad esempio, in un blog dove ogni articolo ha un ID diverso). Express risolve questo problema con i parametri di rotta.
// Esempio di rotta dinamica per un profilo utente
app.get('/utente/:id', (req, res) => {
// Accediamo al parametro ':id' tramite l'oggetto req.params
const userId = req.params.id;
res.send(`Stai visualizzando il profilo dell'utente con ID: ${userId}`);
});
In questo esempio, se visiti http://localhost:3000/utente/42, il server risponderà "Stai visualizzando il profilo dell'utente con ID: 42". Questo rende l'applicazione estremamente flessibile.
I Middleware: Il Motore di Express
Il concetto di Middleware è fondamentale per comprendere Express. Un middleware è essenzialmente una funzione che ha accesso all'oggetto richiesta (req), all'oggetto risposta (res) e alla funzione next nel ciclo di richiesta-risposta dell'applicazione.
Come funziona un Middleware?
Immagina il middleware come una serie di "stazioni di controllo" attraverso cui passa una richiesta prima di arrivare alla destinazione finale. Un middleware può:
- Eseguire qualsiasi codice.
- Modificare l'oggetto richiesta e risposta.
- Terminare la richiesta inviando una risposta.
- Chiamare il middleware successivo tramite
next().
Se un middleware non invia una risposta e non chiama next(), la richiesta rimarrà "appesa" e il browser caricherà all'infinito.
Esempio di Middleware Personalizzato (Logger)
Creiamo un middleware che stampa nel terminale l'ora e il metodo di ogni richiesta in arrivo.
// Middleware di logging personalizzato
const loggerMiddleware = (req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] Richiesta ricevuta: ${req.method} ${req.url}`);
// Fondamentale: passa il controllo al prossimo middleware o rotta
next();
};
// Applichiamo il middleware a tutte le rotte dell'app
app.use(loggerMiddleware);
app.get('/contatti', (req, res) => {
res.send('Pagina Contatti');
});
Middleware integrati
Express fornisce alcuni middleware predefiniti molto utili. Uno dei più importanti è express.json(), che permette al server di interpretare i dati in formato JSON inviati nel corpo (body) di una richiesta POST.
app.use(express.json());
app.post('/api/dati', (req, res) => {
const datiRicevuti = req.body;
console.log(datiRicevuti);
res.status(201).json({ messaggio: 'Dati ricevuti con successo!', dati: datiRicevuti });
});
Esempi Pratici: Creazione di una Mini-API
Per mettere in pratica quanto appreso, costruiamo una piccola API per gestire una lista di libri.
Implementazione di un'API REST
In questo scenario, utilizzeremo un array in memoria per simulare un database.
const express = require('express');
const app = express();
app.use(express.json());
let libri = [
{ id: 1, titolo: 'Il Signore degli Anelli', autore: 'J.R.R. Tolkien' },
{ id: 2, titolo: '1984', autore: 'George Orwell' }
];
// 1. Leggi tutti i libri
app.get('/libri', (req, res) => {
res.json(libri);
});
// 2. Leggi un singolo libro per ID
app.get('/libri/:id', (req, res) => {
const libro = libri.find(l => l.id === parseInt(req.params.id));
if (!libro) return res.status(404).send('Libro non trovato');
res.json(libro);
});
// 3. Aggiungi un nuovo libro
app.post('/libri', (req, res) => {
const nuovoLibro = {
id: libri.length + 1,
titolo: req.body.titolo,
autore: req.body.autore
};
libri.push(nuovoLibro);
res.status(201).json(nuovoLibro);
});
// 4. Elimina un libro
app.delete('/libri/:id', (req, res) => {
libri = libri.filter(l => l.id !== parseInt(req.params.id));
res.send({ messaggio: 'Libro eliminato correttamente' });
});
app.listen(3000, () => console.log('API Libri attiva su porta 3000'));
Analisi del caso d'uso
In questo esempio abbiamo implementato le operazioni CRUD (Create, Read, Update, Delete). Abbiamo utilizzato res.status() per comunicare al client l'esito dell'operazione (es. 404 per non trovato, 201 per creato). Questo è il modo in cui funzionano quasi tutte le moderne applicazioni web: un frontend (React, Vue o Angular) che effettua chiamate a un backend Express che a sua volta interagisce con un database.
Errori Comuni e FAQ
Durante l'apprendimento di Express, è facile incorrere in alcuni errori classici. Ecco i più frequenti:
1. Dimenticare next() nei middleware
Se scrivi un middleware e dimentichi di chiamare next(), la tua applicazione sembrerà "congelata". Il client non riceverà mai una risposta perché la richiesta è rimasta bloccata in quella funzione.
2. Posizionamento errato dei middleware
L'ordine in Express è fondamentale. Se definisci app.use(express.json()) dopo le tue rotte POST, req.body sarà undefined quando proverai a leggerlo nelle rotte. I middleware globali devono sempre essere dichiarati prima delle rotte.
3. Confusione tra res.send(), res.json() e res.end()
res.send(): Invia una risposta generica (testo, HTML, oggetto).res.json(): Forza l'invio di un oggetto in formato JSON e imposta l'headerContent-Typecorretto. È la scelta migliore per le API.res.end(): Termina la risposta senza inviare alcun dato. Utile per le risposte di stato pure.
4. Gestione degli errori 404
Molti principianti si chiedono come gestire le pagine non trovate. In Express, si aggiunge un middleware "catch-all" alla fine di tutte le rotte:
app.use((req, res) => {
res.status(404).send('Ops! Pagina non trovata.');
});
Prossimi Passi e Approfondimenti
Ora che hai le basi di Express.js, sei pronto per scalare le tue competenze. Ecco un percorso consigliato per continuare a crescere:
1. Integrazione con un Database
L'uso di array in memoria è utile per i test, ma per applicazioni reali avrai bisogno di un database. Ti suggerisco di studiare MongoDB (tramite la libreria mongoose) per un approccio NoSQL, oppure PostgreSQL (tramite sequelize o prisma) per un approccio relazionale.
2. Organizzazione del Progetto (MVC)
Quando l'app cresce, mettere tutto in app.js diventa ingestibile. Studia il pattern MVC (Model-View-Controller). Impara a usare express.Router() per separare le rotte in file diversi (es. routes/users.js, routes/products.js).
3. Sicurezza e Autenticazione
Un server esposto al web deve essere sicuro. Approfondisci l'uso di JWT (JSON Web Tokens) per l'autenticazione e librerie come bcrypt per l'hashing delle password. Non dimenticare di installare helmet per proteggere le tue app da vulnerabilità HTTP comuni.
4. Deployment
Prova a pubblicare la tua applicazione su piattaforme come Render, Railway o Heroku per renderla accessibile a tutto il mondo.
Express.js è un punto di partenza incredibile. La sua semplicità ti permette di concentrarti sulla logica di business senza perderti in configurazioni infinite, rendendolo lo strumento ideale per ogni aspirante sviluppatore backend.