Guida Completa a Express.js: Crea il tuo primo Server Web con Node.js

Principiante
JavaScript Node.js

Impara a utilizzare Express.js per costruire applicazioni web robuste e scalabili. Scopri routing, middleware e gestione delle richieste in questa guida completa.

Pubblicato
Tag
Web Development javascript API REST backend nodejs Express.js

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

  1. Crea una cartella per il progetto: Apri il terminale e crea una nuova directory.
  2. Inizializza il progetto: Esegui npm init -y per creare il file package.json, che conterrà tutte le informazioni e le dipendenze del tuo progetto.
  3. 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 tramite res.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ò:

  1. Eseguire qualsiasi codice.
  2. Modificare l'oggetto richiesta e risposta.
  3. Terminare la richiesta inviando una risposta.
  4. 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'header Content-Type corretto. È 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.