Salve a tutti,
oggi vorrei parlarvi di Node.js, un framework event-driven basato sul JavaScript Engine v8 di Google (usato anche da Chrome), che ci permette di utilizzare tale linguaggio anche per attività server-side, grazie alla sua gestione asincrona degli eventi.
Certo, JavaScript è un linguaggio un po' antipatico, e molti all'idea di ritrovarselo anche lato server sicuramente storceranno un po' la bocca, però Node.js è molto potente, leggero e versatile (e open-source!), e ti permette di realizzare delle applicazioni web real-time con molta facilità.
Arriviamo al dunque.
[io uso Windows, quindi farò riferimento a questo sistema operativo]
Scaricate dal sito l'ultima versione stabile di Node.js ed installatelo senza particolari problemi; un dettaglio: compresa nel pacchetto c'è anche l'installazione di NPM, un repository di package javascript che ovviamente sono molto utili (se non necessari) per una piacevole esperienza con Node.js.
Una volta completata l'installazione, verificate che sia andato tutto a buon fine aprendo il prompt dei comandi e digitando node -v
: dovrebbe comparire la versione di Node.js appena installata sul vostro PC. Nel caso non funzionasse, probabilmente c'è bisogno di un riavvio. Nel caso non funzionasse ancora, chiamate Gandalf o chi per lui (non dovrebbe essere necessario, sono sempre riuscito ad installarlo senza problemi al primo tentativo, per buona pace di Gandalf).
Adesso siamo pronti per scrivere la nostra prima applicazione con Node.js!
Creiamo una cartella dove ci pare e piace, con un nome qualsiasi, per esempio "testNode": questa cartella sarà la root della nostra applicazione.
Premessa: Node.js utilizza dei moduli, alcuni inclusi nativamente, altri invece da indicare ed installare tramite NPM. I moduli sono oggetti che racchiudono funzionalità ed estensioni utili a gestire al meglio la nostra applicazione in tutte le sue sfaccettature.
Come facciamo ad indicare questi moduli? Si deve creare nella root un file chiamato package.json
, al cui interno elencheremo le varie "dependencies" relative all'applicazione, oltre ad alcune informazioni generiche della stessa. Di seguito un esempio che può fare al caso nostro:
package.json
{ "name": "testNode", "version": "0.0.1", "description": "applicazione di prova per Node.js - grazie allafinedelpalo.it!", "dependencies": { "express": "~4.13.3", "socket.io": "latest" } }
Commentiamo: all'interno delle "dependencies" ho inserito due moduli, express e socket.io (ne parlerò più avanti), indicando quale versione mi serve (proprio la 4.13.3 o l'ultima disponibile); a questo punto, con il prompt dei comandi posizionatevi nella vostra cartella di root "testNode" (dove c'è anche il file package.json) ed eseguite il comando
1 |
npm install |
Inizieranno quindi ad essere scaricati tutti i vari pacchetti necessari; al termine, nella nostra cartella ne sarà comparsa una ulteriore chiamata "node_modules", dove sono stati installati i moduli da noi richiesti. Nota: questa procedura di installazione si può effettuare anche successivamente, magari perché ci siamo resi conto che abbiamo bisogno di un ulteriore modulo (npm fa il "delta", riesce a capire cosa c'è di nuovo), oppure si può indicare direttamente il pacchetto da aggiungere indicandolo esplicitamente nel comando:
1 |
npm install express |
Iniziamo a scrivere il codice per il nostro server node: creiamo due file, app.js
e index.html
, dentro la nostra folder "testNode"
app.js
const express = require('express'); const app = express(); const http = require('http').Server(app); const io = require('socket.io')(http); const port = 3100; http.listen(port, function(){ console.log('listening on *:'+port); }); app.get(['/','/index'], function (req, res) { res.sendFile(__dirname + '/index.html'); }); io.on('connection', function(socket){ console.log('user connected'); });
Nel dettaglio, abbiamo fatto richiesta del modulo express (riga 1), lo abbiamo instanziato (riga 2) e lo abbiamo associato al Server presente all'interno del modulo nativo http (riga 3); al nostro Server http abbiamo aggiunto anche il modulo socket.io (riga 4) e successivamente gli abbiamo detto di mettersi in ascolto sulla porta indicata (in questo caso io ho utilizzato la 3100 - riga 7).
Express è un framework che estende Node.js con numerose e utili API, semplificandone l'utilizzo; Socket.io è invece un engine per la gestione degli eventi real-time.
La riga 11 indica che, quando facciamo una richiesta GET al percorso '/' o '/index', deve essere preso in considerazione il nostro file '/index.html' (che vedremo sotto) presente nella cartella 'testNode' (la variabile nativa '__dirname' fa riferimento al percorso di root dell'applicazione). In poche parole, se carichiamo (ma non provate ora! Manca un passaggio fondamentale) nel browser le pagine 'http://localhost:3100/' o 'http://localhost:3100/index' vedremo comparire la nostra 'index.html' (se ancora non l'avete creata, ovviamente vi restituirà un errore di "assenza della risorsa").
La riga 15 invece è relativa a socket.io e all'evento di 'connection' da parte di un utente al server (sarà più chiaro più avanti); importante è il parametro socket della funzione di callback.
Bene, abbiamo impostato il nostro Server, adesso facciamolo "partire": tramite prompt dei comandi, posizioniamoci nella nostra cartella "testNode" e digitiamo il comando
1 |
node app.js |
Se non abbiamo fatto errori, comparirà la scritta 'listening on *:3100" - il nostro Server è partito correttamente!
index.html
<!doctype html> <html> <head> <title>Test Node.js - allafinedelpalo.it</title> <script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script> </head> <body> <h1>Ciao Node! - allafinedelpalo.it</h1> </body> </html>
I punti fondamentali di questo codice sono la riga 5 (dove includiamo lo script di socket.io, rigorosamente prima di tutti gli altri) e la riga 7, dove creiamo l'effettiva connessione tramite la funzione io()
di socket.io al nostro server che è in ascolto. Se quindi adesso proviamo a caricare la pagina 'http://localhost:3100/', oltre a comparirvi la scritta sul browser, potrete vedere nel prompt la riga 'user connected' che era stata indicata all'interno della funzione di callback dell'evento 'connection' di socket.io
A questo punto, provate ad aprire altri tab e altri browser caricando la stessa pagina: secondo voi cosa potrete notare nel prompt?
Ora inizia il divertimento.
Fra i vari metodi di socket.io, .on()
e .emit()
sono fra i più importanti.
.io()
l'avete già visto dentro app.js:
1 |
io.on('connection', function(socket){[...]}) |
Praticamente il server è in continuo ascolto sull'evento 'connection' e, al suo scatenarsi, viene eseguita la funzione di callback indicata. Nello specifico, l'evento 'connection' è nativo di socket.io, e viene propagato ogni volta che un client si connette al server, come abbiamo visto in index.html:
1 |
var socket = io(); //invia automaticamente l'evento 'connection' al server |
Modifichiamo quindi app.js in questo modo:
[...] io.on('connection', function(socket){ console.log('user connected'); socket.emit('allafine', { hello: 'world' }); socket.on('delpalo', function (data) { console.log(data.altro); }); }); [...]
...e index.html in quest'altro:
[...] <script> var socket = io(); socket.on('allafine', function(data){ console.log(data.hello); socket.emit('delpalo', { altro: 'valore' }); }); </script> [...]
Ah, importante! Ogni volta che fate una modifica al codice di app.js, dovete stoppare il server (CTRL+C) e farlo ripartire, in modo da prendere le modifiche.
Spiegazione: come detto, nel momento in cui io carico la mia pagina, si crea una connessione al server, e questo si manifesta tramite l'invio (=emit) automatico di socket.io dell'evento 'connection', il quale viene catturato dal server grazie a io.on('connection',[...])
.
Quindi, in generale: ad un .emit('nome_evento')
che viene inviato corrisponde un .on('nome_evento')
che lo intercetta, e questa è la modalità principale con cui avviene la comunicazione server-client e viceversa.
Tornando a noi, viene intercettata la 'connection' e, nella funzione di callback, abbiamo l'oggetto socket che racchiude le API di socket.io e l'istanza di connessione appena creata: app.js e index.html adesso si possono "parlare".
A questo punto, il server invia l'evento chiamato 'allafine' passando anche l'oggetto json {hello: 'world'}
, il quale viene intercettato dal client con il metodo .on('allafine')
e la funzione associata che può gestire i dati che gli sono arrivati: indovinate cosa stampa console.log(data.hello)
nella console del browser...
Allo stesso modo, il client invia l'evento 'delpalo' al server con l'oggetto { altro: 'valore' }
:
That's it! Abbiamo appena fatto scambiare due parole fra il server e il client connesso!
Ovviamente questo esempio è molto semplice, e ci sono tanti altri aspetti da approfondire, tipo l'integrazione con un database MySQL o MongoDB con i rispettivi moduli, o l'utilizzo di metodi presenti in file .js esterni rispetto ad app.js: di seguito un esempio di questo ultimo punto, in cui vogliamo utilizzare il metodo 'metodoEsterno' del file 'fileEsterno.js' dentro la nostra cartella "testNode"
fileEsterno.js
var exports = module.exports = {}; exports.metodoEsterno = function() { console.log("metodoEsterno dentro fileEsterno.js"); };
app.js
[...] var fileEsterno = require('./fileEsterno'); io.on('connection', function(socket){ console.log('user connected'); socket.emit('allafine', { hello: 'world' }); socket.on('delpalo', function (data) { console.log(data.altro); }); fileEsterno.metodoEsterno(); }); [...]
La parola chiave è exports (che è associata a module.exports per praticità), e che viene aggiunta come prefisso ad ogni metodo che vogliamo esporre all'esterno. Nel momento in cui io faccio var x = require('file');
, nella variabile x viene automaticamente salvato l'oggetto module.exports del file richiesto, quindi nel nostro caso la variabile 'fileEsterno' dentro app.js è un oggetto con al suo interno il metodo 'metodoEsterno', che posso così invocare.
Questa gestione ci permette di avere un controllo più accurato del nostro codice, senza appesantire troppo il file app.js.
A voi a questo punto la scoperta di altre curiosità e particolarità di Node.js!
Nel caso, chiedete pure nei commenti.
Ciao ciao
