Tecnologie per lo sviluppo di applicazioni di rete server-side: Javascript, Node.js e NestJS - Unina

Pagina creata da Camilla Santi
 
CONTINUA A LEGGERE
Tecnologie per lo sviluppo di applicazioni di rete server-side: Javascript, Node.js e NestJS - Unina
Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica

Tesi di Laurea Triennale in Ingegneria Informatica

Tecnologie per lo sviluppo di applicazioni di
rete server-side: Javascript, Node.js e
NestJS

Anno Accademico 2019/2020

relatore
Ch.ma prof. Valeria Vittorini
candidato
Dario Guarracino
matr. N46002963
Tecnologie per lo sviluppo di applicazioni di rete server-side: Javascript, Node.js e NestJS - Unina
Indice

1 Architetture per lo sviluppo web                                                   1
  1.1   Architettura Client - Server . . . . . . . . . . . . . . . . . . . . . .      1
  1.2   Architettura multi-tier e Web Server . . . . . . . . . . . . . . . . .        2
        1.2.1   Model View Controller . . . . . . . . . . . . . . . . . . . . .       3
  1.3   Sviluppo di un Web Server . . . . . . . . . . . . . . . . . . . . . . .       4

2 Linguaggi di programmazione e sviluppo client-side                                  5
  2.1   Panoramica e stato dell’arte . . . . . . . . . . . . . . . . . . . . . .      5
  2.2   Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    7
        2.2.1   JS e la manipolazione del DOM . . . . . . . . . . . . . . . .         8
        2.2.2   Single Page Applications . . . . . . . . . . . . . . . . . . . . 10

3 Node.js e lo sviluppo server-side                                                  14
  3.1   V8 e l’approccio event-driven . . . . . . . . . . . . . . . . . . . . . 14
        3.1.1   Blocking e non-blocking I/O . . . . . . . . . . . . . . . . . . 14
        3.1.2   La soluzione di Node.js e l’approccio event-driven . . . . . . 16
  3.2   Web Framework e REST . . . . . . . . . . . . . . . . . . . . . . . . 20
        3.2.1   Framework e principio DRY . . . . . . . . . . . . . . . . . . 20
        3.2.2   Web Framework per le Classical Web Applications . . . . . . 21
        3.2.3   Approccio RESTful e il concetto di Risorsa [6] . . . . . . . . 22
  3.3   Node.js e Express.js . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
        3.3.1   Oltre Express.js . . . . . . . . . . . . . . . . . . . . . . . . . 26

                                          i
INDICE

4 NestJS - Princìpi ed applicazioni                                               28
  4.1   L’architettura a moduli e le dipendenze . . . . . . . . . . . . . . . . 28
  4.2   Caso di studio e implementazione di un Web Server RESTful . . . . 32
        4.2.1   Caso di studio . . . . . . . . . . . . . . . . . . . . . . . . . . 32
        4.2.2   Interfaccia REST . . . . . . . . . . . . . . . . . . . . . . . . 33
        4.2.3   Autenticazione e Autorizzazione . . . . . . . . . . . . . . . . 34
        4.2.4   Approccio Controller - Service . . . . . . . . . . . . . . . . . 35

Appendici                                                                         36

A Implementazione Web Server RESTful in NestJS                                    37

                                         ii
A Mamma e Papà, ai miei fratelli e a Jolie. La mia forza e il mio orgoglio.
Capitolo 1

Architetture per lo sviluppo web

Il 6 Agosto 1991, nei pressi del CERN di Ginevra, Tom Berners-Lee pubblicava e
rendeva al mondo e alla storia il primo sito Web, con poco più 10 righe di testo.
Pochi mesi prima scriveva httpd, il primo server per il Word Wide Web e la
prima versione di HTML, il linguaggio di formattazione de-facto per lo sviluppo
di interfacce web. Mentre da un lato crescevano - e crescono indefinitamente
tutt’oggi – le tecnologie per lo sviluppo client-side, dall’altro si è costantemente
pensato ad architetture, linguaggi e strategie di sviluppo per la parte server-side
di applicazioni web (web application o web-based app in inglese), il cosiddetto
Back-end.[2]

1.1     Architettura Client - Server

L’architettura Client – Server è un’architettura di rete ove le parti comunicanti
sono due o più. Nella sua forma più basilare, il server è un host sempre attivo, che
risponde alle richieste di servizio di molti client. Nel caso di applicazioni web, un
web server, sempre attivo e pronto a ricevere, risponde alle richieste dei browser,
attivi sui client. La richiesta può trattarsi di una risorsa qualsiasi: un file di
testo, html, un’immagine, file audio, testo formattato o altro che possa servire al

                                         1
CAPITOLO 1. ARCHITETTURE PER LO SVILUPPO WEB

client per completare il suo servizio. [8] Nel caso di servizi che devono gestire un
numero molto grande di richieste, la soluzione oggi più utilizzata è quella dello
scalamento orizzontale, cioè allocare più server di potenza sostenuta in modo
che funzionino come un’unica entità logica.

1.2     Architettura multi-tier e Web Server

Nell’ambito web, un Web Server è la parte software in esecuzione sul server e
gestisce le richieste di pagine web inviata dal client attraverso il protocollo http
(o con la versione sicura https) . Facendo riferimento all’architettura multi-tier,
un applicazione web è suddivisa:

   • Logica di presentazione: È il livello più alto: mostra all’utente le operazioni
      che può eseguire e fornisce in output informazioni che l’utente può capire.
      Nelle web application si trova sul browser web del client (front-end)

   • Livello applicazione: Si occupa delle logiche di business di un’applicazio-
      ne, eseguendo operazioni dettagliate e prendendo in input eventuali dati dal
      livello di presentazione. Comunica direttamente con il livello dati per la
      persistenza e la gestione degli stessi. In una web application, il livello appli-
      cazione riguarda il Web Server ed è il livello che genera contenuti dinamici
      da servire al livello di presentazione

   • Livello dati: È costituito da un database che si occupa della gestione e della
      persistenza dei dati e presenta un software che fornisce al livello applicazione
      l’accesso agli stessi.

   Generalmente, il back-end viene visto come l’insieme del livello applicazione
e il livello dati.
   Tra le più famose applicazioni software che implementano web server troviamo:

                                          2
CAPITOLO 1. ARCHITETTURE PER LO SVILUPPO WEB

   • Apache HTTP Server e Apache Tomcat, open source, sviluppati da Apache
     Software Foundation

   • nginx, open source, rilasciato su licenza BSD-Like

   • ColdFusion, distribuita da Adobe

                       Figura 1.1: multi-tier architecture

1.2.1    Model View Controller

Introdotto originariamente dal linguaggio SmallTalk, è un pattern architettu-
rale e si basa sul principio della separazione delle responsabilità (SoC -
Separation of Concerns in inglese) fra i suoi componenti:

   • Model: è il responsabile della gestione dei dati, della logica e delle regole
     applicative; esso definisce la struttura dati dinamica dell’applicazione.

   • View: la rappresentazione visiva del Model (o di più Model), quando si parla
     di web framework, si riferisce al contenuto HTML (CSS e JS) che mostra
     l’informazione all’utente.

   • Controller: è il responsabile della logica di business; riceve i dati di input
     all’utente e manipola il model, per poi rispondere a sua volta attraverso le
     View.

                                        3
CAPITOLO 1. ARCHITETTURE PER LO SVILUPPO WEB

                Figura 1.2: Diagramma delle interazioni nel MVC

   Il Model View Controller oggi è un pattern standard adottato dalla maggior
parte dei framework back-end, ma si estende a qualsiasi tipo di applicativo, e
le interazioni tra i suoi componenti dipendono fortemente dalle tecnologie e dai
linguaggio di programmazione con cui si opera.

1.3     Sviluppo di un Web Server

Il problema che ci poniamo in questo elaborato è lo studio del linguaggio Java-
Script e le sue applicazioni client-side, per poi proseguire con l’analisi di sviluppo
di un web server leggero con l’utilizzo di Node.js, un ambiente di runtime Java-
Script che permette di costruire applicazioni server-side. Studieremo le principali
differenze tra Node.js e i web server quali Apache e nginx, lo sviluppo attraverso
il linguaggio Javascript e Typescript e l’utilizzo di frameworks come Express.js e
NestJS. Concluderemo poi con un’analisi sullo sviluppo di un’interfaccia API con
GraphQL e le principali differenze con l’approccio REST.

                                          4
Capitolo 2

Linguaggi di programmazione e
sviluppo client-side

2.1     Panoramica e stato dell’arte

Lo sviluppo di un software include ovviamente la scelta di uno o più linguag-
gi di programmazione da utilizzare. Nell’ambito dei web server, i linguaggi di
programmazioni che hanno avuto maggior rilievo negli ultimi anni sono:

   • PHP : (acronimo ricorsivo di « PHP : Hypertext Prepocessor), linguaggio
      di scripting interpetato, attualmente quello più utilizzato nel panorama
      web, sia per la semplicità di utilizzo che per l’uso che ne fanno i Content
      Management Software (CMS) come WordPress, Magento, Shopify. . .

   • Java : linguaggio de-facto per la programmazione orientata agli oggetti
      e l’utilizzo di design patterns specifici. Particolare importanza hanno i
      framework utili allo sviluppo web come Spring e Hibernate.

   • Python : anch’esso un linguaggio di scripting interpretato e orientato agli
      oggetti ma in generale multi-paradigma, molto usato anche in ambito di
      calcolo numerico.

                                        5
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                               CLIENT-SIDE

   • .NET : più che un linguaggio, è una piattaforma general purpose, che tra
     i tanti strumenti include ASP.NET, insieme di tecnologie per lo sviluppo
     di servizi Web attraverso i linguaggio di programmazione che supportano il
     .NET Framework, come C# e Visual Basic .NET

Figura 2.1: Percentuale dei siti web che usano un linguaggio di programmazione
server-side, aggiornato al 13 Febbrario 2019

   I linguaggi sopracitati, oltre a fornire strumenti per lo sviluppo di altro genere
di software, formano in larga parte ciò che ha costituito il Web 2.0, e cioè un
approccio al web più dinamico: la creazione e l’editing di contenuti attraverso
i Content Management Software, l’E-Commerce, il Blogging e i Social Network,
hanno trasformato la funzione del web da semplice strumento di consultazione
a piattaforma dove poter contribuire popolando lo stesso con i propri contenuti.
[10] A contribuire allo sviluppo del Web dinamico è sicuramente stata la tecnica
AJAX (Asynchronous JavaScript and XML), che ha consentito alle pagine
web, prima statiche, la comunicazione fra web browser e server attraverso il lin-
guaggio di scripting JavaScript. La rivoluzione di questa tecnologia è stata per
l’appunto l’Asynchronous, cioè il caricamento asincrono dei dati dal server senza
l’interruzione del flow della pagina corrente. Ciò ha in primis ottimizzato l’uti-
lizzo della banda, evitando il ricaricamento superfluo del contenuto HTML delle

                                         6
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                                CLIENT-SIDE

pagine ad ogni interazione e, di conseguenza, migliorato di gran lunga l’esperienza
dell’utente.

          Figura 2.2: Schema di funzionamento di una richiesta AJAX

2.2     Javascript

Inizialmente sviluppato da Brendan Eich della Netscape Communications con
il nome di Mochan/LiveScript e successivamente rinominato JavaScript, è stato
standardizzato nel 1997 dalla ECMA con il nome di ECMAScript. JavaScript
(talvolta abbreviato in JS) - che non ha nessuna relazione con Java se non per
le sintassi più o meno simili - è un linguaggio di scripting a tipizzazione debole
orientato agli oggetti e agli eventi, dove l’uso di questi ultimi è particolarmente
frequente nello sviluppo web client-side.

                                            7
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                               CLIENT-SIDE

                 Figura 2.3: Hello World in JavaScript e HTML

2.2.1    JS e la manipolazione del DOM

È nella logica di presentazione che infatti JavaScript trova il suo maggior uso:
inserito nei file HTML delle pagine web, consente di accedere e modificare il
DOM (Document Object Model). Il DOM è un modello strutturale a oggetti,
una forma di rappresentazione dei documenti di cui anche i browser si servono co-
me interfaccia per l’accesso agli elementi della pagina. La pagina è rappresentata
come un albero e ogni ramo porta a un nodo, ogni nodo contiene degli oggetti.
JavaScript, attraverso l’interfaccia window, supporta l’accesso diretto al DOM,
permettendo così la sua manipolazione per costruire interfacce grafiche. Partico-
lare successo ha avuto jQuery, una libreria open-source JavaScript che facilita
la gestione degli eventi, la manipolazione del DOM e le animazioni degli elemen-
ti con un approccio modulare, offrendo una grande estensibilità da parte della
community. Al 2018, jQuery è la libreria JavaScript più utilizzata sul Web [9].
   Tuttavia, con le recenti versioni di JavaScript e dei browser, le funzionalità
che rendevano jQuery un must per gli sviluppatori, sono diventate funzionalità

                                        8
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                               CLIENT-SIDE

Figura 2.4: Percentuale di siti che usano varie librerie JavaScript. Aggiornato al
3 Ottobre 2019

                                        9
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                               CLIENT-SIDE

standard del linguaggio, rendendo quindi la libreria uno strumento da cui poter
prescindere.

2.2.2    Single Page Applications

Nell’Aprile 2002, Stuart Morris scrisse il primo sito self-contained su slash-
dotslash.com, compatibile esclusivamente con Internet Explorer e utilizzando le
tecnologie di quel tempo: niente AJAX, niente frameworks, solo DHTML, un’in-
sieme di tecnologie che permettevano di cambiare dinamicamente il contenuto delle
pagine attraverso direttive JavaScript e CSS. Dimostrò cosa si poteva fare con le
tecnologie di allora e mostrò un primo esempio di Single Page Application [3]. Le
SPA sono web-application (o siti web) che non hanno bisogno di essere ricaricate:
l’utente interagisce con la pagina, che si aggiorna dinamicamente anche durante
la navigazione inter-sito, caricando eventuali dati dal server. La web-application
diventà così più simile a una Desktop-App o una Mobile-App, con il vantaggio
di ottimizzare la banda usata e migliorare l’esperienza utente.

Javascript Frameworks per le SPA

Numerosi framework JavaScript hanno adottato i principi SPA, tra i più utilizzati
troviamo:

   • AngularJS: il più usato tra i framework front-end; è stato sviluppato da
     Google e si basa sul pattern MVC e MVVM (Model View-View Model ) e
     sul principio di Bidirectional UI Data Binding: ogni qual volta il model
     viene modificato, la view associata si aggiorna e viceversa. È scritto e può
     essere usato con TypeScript utilizzando, per le view, un HTML esteso
     con direttive riservate che poi verrà compilato in HTML puro. Più avanti
     torneremo sulla struttura di Angular e su Typescript quando parleremo dei
     framework server-side.

                                        10
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                           CLIENT-SIDE

• EmberJS: basato completamente sul MVC ; anch’esso implementa il Bi-
  directional Data Binding e una semplice gestione dei template attaverso
  Handlebars.js.

• React: il più recente tra questi. Più che un framework è una libreria
  per costruire interfacce utente. Usa un linguaggio misto tra HTML e
  JS, JSX e offre un potente strumento per creare ed estendere componenti
  grafici. Usato con librerie di store management come Redux o MobX,
  React permette lo sviluppo di applicazioni complesse. È inoltre il motore
  di React Native, il framework per costruire applicazioni mobile native per
  iOS e Android.

                                   11
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                               CLIENT-SIDE

                           (a) Fetching dei dati in React

                    (b) Implementazione di una tabella in React

   Con l’affermazione dei framework e delle librerie client-side, che garantiscono
un maggiore controllo sul codice, un’architettura più solida e strumenti di testing

                                        12
CAPITOLO 2. LINGUAGGI DI PROGRAMMAZIONE E SVILUPPO
                                                CLIENT-SIDE

più efficienti, unita anche alla crescita della velocità dei calcolatori e dei browser
nel processare gli script, lo sviluppo web si sta via via orientando verso una più
netta separazione tra front-end e back-end.
   Dapprima (e in larga parte tutt’ora), il back-end si occupava di tutta la logica
di business, compreso il rendering delle pagine HTML che restituiva nella risposta
HTTP. Questo tipo di architettura è detta Thick server architecture, in cui
tutto il peso delle operazioni grava sul server, che ricostruisce lo stato del client a
partire dalla sua richiesta (Stateless) o salvandolo stesso in memoria (Stateful).
   Con le Single Page Applications invece, l’archiettura back-end diventa di ti-
po Thin server architecture: tutta la logica applicativa passa al client, che si
occupa di aggiornare l’interfaccia e ricavare esclusivamente i dati formattati dal
server. Il server quindi diventa una vera a propria API o Web Service, attra-
verso la quale si può accedere alle risorse e manipolarle a seconda dei permessi
dell’utente. Si riduce così di gran lunga il carico di lavoro sul server, permettendo
così una completa separazione tra i due fronti a patto di stabilire un contratto di
comunicazione (REST, GraphQL...) e facilitando quello scalamanto orizzontale
di cui abbiamo parlato inizialmente.

                                          13
Capitolo 3

Node.js e lo sviluppo server-side

3.1      V8 e l’approccio event-driven

Node.js è un run-time system di JavaScript, open source, multipiattaforma
e orientato agli eventi, costruito sul motore JavaScript V8 di Chrome. Un
run-time system è un software che fornisce i servizi necessari all’esecuzione di
un programma. V8 è un motore open-source sviluppato da Google, scritto in
C++ che interpreta codice JavaScript e WebAssembly. È il principale motore
di scripting di Google Chrome ed è stato adottato da Ryan Dahl, creatore di
Node.js, per portare JavaScript lato server.
   La scelta di adottare V8 come motore per Node.js è stata quella di usare un
linguaggio già ben conosciuto per il suo sviluppo client-side e che si adattasse per
costruzione all’approccio event-driven

3.1.1     Blocking e non-blocking I/O

Una system call (chiamata di sistema) è una richiesta, da parte di un processo
a livello utente a un servizio di livello kernel. Una system call può riguardare
il controllo dei processi (wait(), signal(), execute(), fork(), ..., la gestione del file
system (read(), open(), write(), ...) o il networking(accept(), listen(), socket(),

                                           14
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

... . Una system call I/O è una chiamata di sistema che implica la comunicazione
con dispositivi esterni (scheda di rete, terminale, hard-drive) e generalmente i
linguaggi di programmazione le implementano come bloccanti (blocking): quando
un processo chiama una system call I/O di livello kernel, questi si occuperà di
contattare il dispositivo ed eseguire le operazioni necessarie di lettura/scrittura. In
questo lasso di tempo (di solito nell’ordine dei microsecondi, nel caso di dispositivi
di rete anche millisecondi), il programma chiamante si mette in attesa della
risposta del kernel, che a sua volta aspetta la risposta il dispositivo.

Approccio multi-threading e multi-processo

In molte applicazioni, generalmente quelle che sfruttano processi CPU-bound,
questa attesa non è un problema perché il programma non deve svolgere altre
operazioni se non aspettare che l’I/O sia concluso. Ma in altre applicazioni, come
nello sviluppo di rete (network programming) in cui ci sono numerosi client
che attendono di essere serviti, il programma potrebbe voler sfruttare quel tempo
di attesa per elaborare gli input degli altri client. Tutto ciò si può risolvere usando
un approccio multi-thread o multi-processo, in cui per ogni richiesta da parte
di un client vengono allocate risorse per un thread/processo che si occupi di quella
richiesta. I linguaggi che adottano questi approcci sono Java per il multi-threading
e PHP/Ruby per il multi-processo e presentano infatti una programmazione
sincrona (synchronous programming) , in cui le istruzioni vengono eseguite una
dopo l’altra così come vengono scritte. Per quanto riguarda i web server questo è
l’approccio finora maggiormente usato.
   Questa soluzione tuttavia inizia a presentare dei limiti di risorse quando il
numero di richieste concorrenti inizia ad aumentare: il server potrebbe trovarsi
saturo di memoria per l’allocazione delle risorse (nell’approccio multi-processo ciò
è ancora più accentuato dal fatto che il fork dei processi richieda più memoria) e
di conseguenza la capacità del numero di richieste al secondo cala drasticamente.

                                          15
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

 Figura 3.1: System Call I/O Bloccanti (a) e System Call I/O non bloccanti (b)

   Non-blocking I/O Una soluzione alternativa a quella dell’uso di thread mul-
tipli è quella della programmazione asincrona con chiamate di sistema non
bloccanti. Una chiamata di sistema non bloccante restituisce immediatamente
il controllo al chiamante. Il processo chiamante sarà poi informato (attraverso
un interrupt) quando l’I/O sarà terminato. Questo permette al programma di
eseguire altre istruzioni mentre l’operazione di I/O è in corso ed elaborare quindi
altre richieste. [7]

3.1.2     La soluzione di Node.js e l’approccio event-driven

Node.js utilizza un’architettura chiamata Single Threaded Event Loop, e si
serve del meccanismo delle callback, cioè una funzione che viene passata come
parametro ad un’altra funzione per poi essere chiamata in un secondo momento
quando si verifica un evento. Il Single Threaded Event Loop funziona in questo
modo:

   • Il Client invia una richiesta al Web Server.

   • Il Web Server Node.js gestisce internamente un Thread-pool limitato per
      servire le richieste dei client.

   • Il Web Server riceve queste richieste e le inserisce in una coda, chiamata
      Event Queue.

                                         16
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

             Figura 3.2: Schema di funzionamento dell’Event Loop

   • Node.js si serve di un Single Thread in cui gira l’Event Loop, un loop
     indefinito che riceve le richieste e le processa.

   • L’Event Loop controlla se ci sono richieste client nella Event Queue e nel
     caso le processa, altrimenti resta in attesa.

        – Se ci sono richieste, ne prende una e inizia a processarla.

        – Se la richiesta non necessita di alcuna operazione I/O Bloccante, la
          processa completamente e invia la risposta al client.

        – Se la richiesta necessita di qualche operazione I/O Bloccante, come
          l’interazione con un Database, File System o servizi esterni allora esegue
          i seguenti passi:

            ∗ Controlla la disponibilità di thread dal Thread-pool e se c’è, prende
               un thread e gli assegna quella richiesta.

            ∗ Il thread si occupa di processare la richiesta, effettuare le opera-
               zioni bloccanti e inviare la risposta all’Event Loop attraverso la
               Callback.

            ∗ L’Event Loop invia la risposta al Client.

Vantaggi e svantaggi

Come si traduce questo approccio rispetto alle performance? Come esem-
pio, consideriamo un caso in cui ogni richiesta al web server richiede 50ms per

                                         17
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

     Figura 3.3: Esempio di codice di lettura di un file Sincrona e Asincrona

essere completata, di cui 45ms sono per una chiamata I/O al database che può
essere eseguita in maniera asincrona. La scelta di eseguire operazioni asincrone
non bloccanti ci fa risparmiare fino a 45ms per ogni richiesta e sfruttarli per gestire
altre richieste.   [5]. Lo svantaggio di questo approccio sta principalmente nello
gestire le operazioni che richiedono un intenso lavoro sulla CPU (manipolazione
di dati, hashing, ecc.), dato che le operazioni vengono fatte in un singolo thread e
ogni blocco di istruzioni mette in attesa il successivo.
   Ciò è stato in parte risolto con l’introduzione del multi-threading dalla versio-
ne di Node.js 10.5.0 dove, come spiegato sopra, per gestire i task CPU-bound ven-
gono creati dei worker thread, ma ciò è possibile solo in un ambiente multi-core
in quanto Node permette l’utilizzo di un core per un solo thread. Risulta quin-
di una scelta meno indicata per applicazioni che richiedono intense operazioni
CPU, come l’Image Processing e Data Manipulation.
   Questo tipo di approccio I/O-driven, rende Node.js fortemente robusto per
applicazioni che richiedono un elevata performance I/O quali:

   • Real-time Web apps come chat o tool di collaborazione e per browser
      game, che facendo uso di Web Socket, richiedono un elevata disponibilità
      da parte del server.

   • Dispositivi IoT: anche qui c’è un flusso di dati real-time e le richieste I/O

                                          18
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

     sono molto frequenti.

   • API Server: Node.js risulta molto efficiente nel gestire numerose richieste
     I/O-bound come le operazioni al database. In particolare è molto usato
     con interfacce REST e GraphQL

   Un ulteriore punto di forza di Node.js è l’utilizzo di JavaScript come linguaggio.
La sua scelta è stata dovuta principalmente al fatto che JavaScript è nativamente
un linguaggio event-driven e supporta la gestione delle callback, oltre al fatto di
essere un linguaggio con una grossa community alle spalle e molti anni di sviluppo.
Node.js, con questa scelta, permette di implementare il paradigma "JavaScript
everywhere", dove sia il front-end che il back-end sono sviluppati intorno ad un
unico linguaggio. Particolarmente utilizzati sono gli stack MEAN e MERN. Il
primo, (Mongo-Express-Angular-Node) è uno stack di sviluppo che include
tutte le tecnologie necessaria per lo sviluppo di un’applicazione che includa una
web app e un web server, entrambi scritti in JavaScript. Il secondo è una
variante del primo con l’utilizzo di React invece di Angular.
   Inoltre, con il rilascio delle nuove versioni di EcmaScript (ES6, ES7 e da poco
anche ES8), uno degli svantaggi di Node.js, il Callback Hell è stato risolto. Il
Callback Hell è la situazione in cui, per via dell’approccio event-driven e dell’u-
tilizzo delle callback, il codice viene innestato man mano rendendolo praticamente
illegibile. Con l’introduzione delle Promise il problema è stato in parte risol-
to e il codice reso più leggibile; successivamente, con l’inserimento dei costrutti
Async/Await, la struttura del codice diventa totalmente simile a quella di codice
sincrono, sfruttando al tempo stesso la potenza della programmazione asincrona.

                                        19
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

                                         (b) Implementazione con Promise-
        (a) Implementazione con Callback Then-Catch

                       (c) Implementazione con Async/Await

        Figura 3.4: Tre diverse implementazioni asincrone in JavaScript

3.2     Web Framework e REST

3.2.1    Framework e principio DRY

Nello sviluppo di web server moderni e in generale di applicazioni web, ad oggi
è molto frequente l’utilizzo di framework. Un Framework in informatica è
in generale una struttura preimpostata, un’architettura logica su cui progettare
un software, con a disposizione libreria di codice pre-esistenti. Il principio base
di un framework è il DRY (Don’t Repeat Yourself), che indica un approccio di
programmazione basato sulla non ridondanza e sul riuso del codice. Un’altro
principio su cui si basano i framework e le librerie è il detto Don’t Reinvent
the Wheel : se il problema da affrontare è già stato risolto con una tecnologia (in
questo caso si parla di libreria di codice) sviluppata da qualcun altro in un modo

                                        20
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

migliore, già funzionante e testato, non perdere tempo a crearne una nuova.

3.2.2    Web Framework per le Classical Web Applications

Un Web Framework dunque, è un framework progettato per supportare lo svi-
luppo di applicazioni web, siti web dinamici e servizi web. Il panorama di web
framework è molto vasto e la scelta di usarne uno dipende fortemente dal tipo di
progetto, dalle tecnologie e dalle risorse (anche umane) a disposizione. Attualmen-
te la maggior parte dei siti internet sono costruiti seguendo l’approccio classical
web application, in cui il server si occupa di tutte le logiche di business e di
creare il contenuto HTML della pagine da restituire al client, il cui unico valore
è quello presentazionale. Una variante di questo approccio, ormai ampiamente
utilizzato è l’AJAX web application, dove il client, attraverso richieste AJAX
(come spiegato prima ad esempio con l’uso di jQuery) permette di rendere il con-
tenuto dinamico e di interrogare il server senza il bisogno di ricaricare la pagina.
Molti framework moderni sono nati seguendo questo approccio, tra i più usati
troviamo: Spring e Struts (Java), Laravel, Yii2, Zend Framework (PHP),
Django (Python) e molti altri altrettanto validi. Questi framework, quasi tutti
fatti per seguire il pattern MVC, oltre a fornire ampie librerie utili come quelle
per l’accesso al Database e al File System, auth provider per l’autenticazione
via Facebook, Google, Twitter ecc., strumenti di debugging e testing, offrono
anche dei template engine, cioè strumenti di templating per agevolare la crea-
zione dei contenuti HTML implementando costrutti di programmazione (if, for,
while, etc...) e rendendo il codice più pulito e leggero.
   Tuttavia, con l’avvento delle applicazioni mobile, delle Single Page Applica-
tions che abbiamo discusso prima e della notevole crescita dei sistemi distribuiti
(ad esempio i microservizi ), un approccio classical non è più quello ideale: la
gestione delle pagine e dei contenuti, la gestione e il salvataggio delle chiavi di

                                         21
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

autorizzazione, la comunicazione con servizi terzi, tutto ciò può diventare una
prerogativa del client. Lo sviluppo mobile e delle SPA ha messo in conto di que-
sti problemi, dove il bisogno che ha un app di comunicare col server c’è quando
bisogna di creare, leggere, aggiornare ed eliminare dati (CRUD), autenticarsi al
sistema ed esserne autorizzati e più in generale eseguire azioni che includano il
mutamento o la lettura delle informazioni che gestisce il server. L’approccio ai
microservizi ha accentuato ancora di più il bisogno di rendere il server quanto
più leggero possibile, migrando da un’unica entità monolite a più piccole entità,
ciascuna delle quali operante ad un servizio dedicato: ancora qui, Separation of
Concerns.

3.2.3    Approccio RESTful e il concetto di Risorsa [6]

REST, Representational State Transfer, è uno stile architetturale per siste-
mi software distribuiti e indica una serie ci principi per la progettazione di Web
Service. Esso si basa sul concetto di Risorsa, un entità che è accessibile e trasfe-
ribile tra server e client. Di solito è l’oggetto appartenente al dominio che stiamo
trattando, dunque:

   • Un studente universitario

   • Un libro di un negozio online di libri

   • Un articolo di un blog

   • La reazione di utente Facebook ad un post

   Nell’interazione tra client e server, quello che viene trasferito è una rappre-
sentazione dello stato interno della risorsa: la rappresentazione può essere
costruita in diversi formati, tra cui i più utilizzati JSON e XML , ma anche con-
tenuto HTML. Il REST si appoggia al protocollo HTTP e sfrutta i suoi metodi
di richiesta per stabilire il tipo di interazione. Il metodo rappresenta un tipo di

                                        22
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

azione e l’URL (o in gergo, Endpoint) indica la risorsa o l’insieme di risorse su
cui effettuare l’azione. I metodi supportati dal protocollo e utilizzati da REST
sono:

   • GET: Ottiene una risorsa o un insieme di risorse

   • POST: Crea una risorsa

   • PUT: Sostituisce una risorsa esistente con una nuova

   • PATH: Sostituisce parti di una risorsa

   • DELETE: Elimina una risorsa
    [b!] GET students       recupera una rappresentazione di un’insieme di studenti

    GET students/1          recupera uno studente con identificativo 1

    POST students           Crea una risorsa studente.

    DELETE students/1 Elimina lo studente con identificativo 1
   Attraverso quindi una semplice richiesta HTTP, con un Endpoint, gli header
necessari e un eventuale body, i client possono accedere e manipolare i dati (o
un’astrazione di essi) presenti sul database con la semplice comunicazione REST
con il server. Un servizio che implementa l’architettura REST si dice RESTFul
Web Service ed è l’approccio maggiormente utilizzato oggi per applicazioni gestio-
nali, web app e app mobile, dove la maggior parte delle azioni sui dati rientrano
nella categoria CRUD (Create, Read, Update, Delete.
   Tra i web framework presentati prima, quasi tutti ad oggi hanno implementato
le librerie necessarie per creare delle API RESTful, permettendo quindi uno
sviluppo client-side orientato alle Single Page Applications.

3.3     Node.js e Express.js

Node.js, come già discusso, si presenta come uno strumento per costruire servizi
web ad alte prestazioni I/O e l’approccio REST è tra i più utilizzati con Node. Il

                                        23
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

Figura 3.5: Schema di interazione tra un client e un RESTful Web Service MVC

framework più esteso e maggiormente scelto per lo sviluppo sia di web server com-
pleti che di servizi web è sicuramente Express.js: il suo payoff sul sito ufficiale
dice Express - Framework web veloce, non categorico e minimalista per Node.js; e
sono proprio i termini non categorico e minimalista che lo caratterizzano. In parti-
colare il framework non forza l’utilizzo di nessun pattern architetturale, lasciando
allo sviluppatore la scelta di implementarne o meno qualcuno. Ovviamente, da
grandi poteri derivano grandi responsabilità e uno sviluppatore alle prime armi
potrebbe cadere in approcci anti-pattern più facilmente, per cui è sempre ne-
cessaria una buona conoscenza dei pattern architetturali. Un secondo punto forte
è l’utilizzo dei middleware, che rendono Express.js altamente estensibile e mo-
dulabile: infatti espone proprio la funzione app.use() attraverso cui si configura
Express per l’utilizzo di un middleware. Per implementare un semplice servizio
RESTful con Express.js, basterà definire, per ogni Endpoint "route", il metodo
HTTP che accetta e l’handler da eseguire una volta ricevuta una richiesta da un
client. Express.js farà sapere all’Event Loop di Node.js che una volta ricevuta la
richiesta dal client, la callback da chiamare è proprio l’handler definito per quel-
l’endpoint. Nell’handler va quindi la logica di business che implementeremo per
gestire la richiesta e manipolare eventuali dati. Ovviamente, se si vuole seguire
un approccio al codice più pulito e modulare, si potrebbe pensare di creare un

                                        24
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

Controller per ogni Risorsa e delegare come handler della richiesta proprio il
metodo del controller (es. controller.create() per la POST, controller.update() per
la PATCH, controller.getOne() per la GET e controller.delete() per la DELETE);
metodo che poi potrà fare utilizzo di un Model o di una Repository per manipo-
lare il dato vero e proprio. Da notare come non è stato menzionato il componente
View, poiché nell’approccio RESTful, la View non è altro che la rappresentazione
del dato che viene restituito al client dal controller e che può avere diverse forme
(generalmente in REST viene usato JSON come formato per lo scambio di dati).
   Dunque si può dire che in REST viene un po’ a cadere il concetto vero è proprio
di MVC, dato che l’interazione View-Controller e View-Model non è più diretta
ma muta sotto forma di richiesta e risposta HTTP.

            Figura 3.6: Codice di esempio di un server con Express.js

   Lo sviluppo con Express.js non si ferma ovviamente all’implementazione di un
servizio RESTful: il framework si può usare per implementare anche un web server
classico con template engine (express-handlebars, Jade, ecc.) o un microservizio
con protocolli dedicati.

                                        25
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

                  Figura 3.7: Middleware pattern in Express.js

3.3.1    Oltre Express.js

Il minimalismo di Express.js lo rende un framework non solo altamente dinamico,
ma anche una base di partenza per poter sviluppare infrastrutture al di sopra di
esso. Seguendo questo modello, sono nati altri framework per Node.js, più robusti
e con un’architettura consolidata: tra questi troviamo LoopBack e NestJS. In
particolare ci focalizzeremo su quest’ultimo nell’ultimo capitolo.

TypeScript

Formalmente, è un linguaggio di programmazione sviluppato da Microsoft, a livello
è pratico è un super-set di JavaScript, ma a tipizzazione statica. Basa le sue
caratteristiche sullo standard ES6, che già contiene il supporto alle classi e altri
costrutti utili come l’Object Destructuring e l’Async/Await; ma ciò che rende
TypeScript (abbreviato in TS) preferibile a ES6 nello sviluppo di progetti in larga
scala, è appunto la tipizzazione. Il linguaggio verrà poi ricompilato in JavaScript
per poter essere interpretato da un web browser o, nel caso di Node.js, da V8. In
particolare TypeScript aggiunge a ES6 le seguenti funzionalità:

   • Firma dei metodi

                                        26
CAPITOLO 3. NODE.JS E LO SVILUPPO SERVER-SIDE

   • Interfacce

   • Moduli

   • Tipi di dato opzionali

   • Tipo Enum e altri utili

   • Generics: la parte più importante, ereditata da linguaggi come Java e C#,
     che permette di creare componenti che funzionino per più tipi dinamicamente
     invece che per uno solo.

Adottato ormai dalla maggior parte delle librerie e framework più utilizzati (anche
lato frontend da Angular e React), TypeScript si pone l’obbietivo di essere non un
sostituto, ma un sovrainsieme di ES6 (a sua volte sovrainsieme di ES5), portando
la fase di sviluppo ad un livello più avanzato al pari di linguaggi come appunto
Java e C.
   Nel prossimo capitolo studieremo il framework NestJS, il suo utilizzo con Type-
Script e proveremo a creare un piccolo Web Server con RESTFul API con l’utilizzo
di tecniche avanzate come gli Interceptor e le Pipe.

                                        27
Capitolo 4

NestJS - Princìpi ed applicazioni

Negli ultimi anni, grazie anche a Node.js e al paradigma JavaScript Everywhere,
JavaScript è diventata la lingua franca del web, sia frontend che backend.
   Ciò che ha reso JavaScript (e in seguito Node.js) un linguaggio molto discusso
e criticato da parte della community di sviluppatori è stata proprio l’eccessiva de-
mocrazia del linguaggio, che pecca di architetture e pattern nativi da imporre allo
sviluppatore al fine di produrre un software più robusto. Mentre progetti come
Angular e React hanno incrementato la produttività dello sviluppatore e creato e
opportunità per uno sviluppo di applicazioni veloci, testabili ed estensibili, nono-
stante la presenza di potenti librerie e tool per Node e con una grossa community
alle spalle, uno dei problemi fondamentali non è stato risolto: l’architettura.
   A questo proposito, NestJS fornisce un application architecture out-of-the-box
(in gergo, preconfigurato) che consente allo sviluppatore di impostare applicazioni
altamente testabili, scalabili e loosely coupled. [4].

4.1      L’architettura a moduli e le dipendenze

NestJS prende ispirazione dall’architettura modulare di Angular, proponendo quin-
di un’architettura basata sui moduli: ogni modulo, configurabile dichiarativamen-

                                           28
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

             Figura 4.1: Schema di un application graph in NestJS[4]

te o imperativamente, contiene il set di funzionalità per cui è stato progettato,
dividendo quindi l’applicazione in tanti moduli (tutti sotto un unico modulo, il
Root module), incapsulati con le proprie responsabilità (SoC ). I moduli possono
avere relazioni di dipendenza con altri moduli, dipendenza che è gestita dal
framework attraverso la Dependency Injection (altro approccio ripreso da An-
gular). Nest, attraverso la configurazione dei moduli e delle loro dipendenze, riesce
a creare un application graph, cioè una struttura dati in cui gestisce i moduli e
l’iniezione delle relative dipendenze.

I Providers e l’Injectable: services, repositories e altro

I provider sono un importante concetto ripreso da Angular: un provider è un
modo di indicare al sistema di Dependency Injection che una classe è iniettabile
(injectable). Queste classi possono essere Services, cioè oggetti (di tipo single-
ton) che offrono l’accesso a metodi per la gestione dei dati o per la comunicazione
con altri componenti. Spesso il service si serve di una Repository che, associata
ad un Model, espone i metodi e gli attributi per manipolare il dato associato e
quindi di comunicare direttamente con il database. Attraverso l’uso delle Repo-
sitory, possiamo implementare infatti il Repository Pattern. Un’altro oggetto

                                         29
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

             Figura 4.2: Relazioni di dipendenza fra i componenti[4]

injectable può essere una classe Factory, che astrae la creazione di una classe
associata e permette di implementare il Factory Pattern.

Interceptors

Una delle tecniche che ha ispirato NestJS è stata l’uso dell’Aspected Oriented
Programming (AOP - Programmazione orientata agli aspetti), un paradigma
basato sulla creazione di entità software, gli aspetti. Una delle peculiarità della
Programmazione Orientata gli Oggetti (OOP) è la modellazione del programma
come uno scambio di messaggi tra oggetti, che sono entità fra loro indipenden-
ti. Questo principio garantisce la modularità del sistema software ma allo stesso
tempo rende complicata l’implementazione di funzionalità che sono comuni a più
oggetti. Si pensi ad un componente Logger che si deve occupare di fare il logging
delle operazioni che avvengono nello scambio fra gli oggetti. Invece di implemen-
tare la funzionalità di logging a tutti gli oggetti, si può pensare di implementare
un aspetto, cioè una parte di codice iniettabile su tutti gli oggetti che hanno
bisogno di questa funzionalità (i Controller ad esempio, che effettuano il logging
del contenuto di una richiesta). In Nest questo è possibile grazie agli Interceptor,
una classe iniettabile, che permette di aggiungere le seguenti logiche all’oggetto su

                                         30
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

     Figura 4.3: Schema di una richiesta GET con l’uso degli Interceptor[4]

cui si inietta, permettendo di

   • Estendere il comportamento di una funzione

   • Aggiungere funzionalità extra prima o dopo l’esecuzione di un metodo

   • Trasformare il risultato di una funzione

   • Trasformare l’eccezione di una funzione

   • Sostituire completamente il comportamento di una funzione sotto determi-
     nata condizioni (ad esempio per implementare un sistema di Cache).

   Se pensiamo allo sviluppo REST, un interceptor si interpone fra la richie-
sta del client e l’handler che gestisce la richiesta (ad esempio il Controller) per
effettuare una delle operazioni elencate sopra. Ad esempio possiamo pensare di
usare un interceptor per trasformare la rappresentazione di una generica risorsa,
restituita del controller, per nascondere campi e informazioni che l’utente non è
autorizzato a leggere.

Middlewares

I Middleware sono una tecnica già presente nel framework di base Express.js,
e sono funzioni che vengono chiamate prima dell’esecuzione dell’handler; essi

                                        31
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

vengono eseguiti a cascata attraverso la chiamata next() e possono:

   • Eseguire qualsiasi tipo funzione

   • Effettuare cambiamenti di informazioni negli oggetti richiesta e risposta

   • Terminare immediatamente la richiesta, ad esempio per mancata autorizza-
      zione.

   • Chiamare il prossimo middleware nello stack

I middleware possono implementare meccanismi di Autenticazione e di validazione
della richiesta e la loro differenza con gli interceptor è che i middleware possono
operare solo prima dell’esecuzione dell’handler e non hanno modo di accedere al
risultato della richiesta sebbene possano accedere all’oggeto response. Gli inter-
ceptor invece, possono operare sia prima che dopo la richiesta, modificando sia il
risultato che i dati in input.

Pipes

Sono una sottocategoria di middleware che si occupano di trasformare i dati
in input di una richiesta o di convalidarli. Conviene quindi implementare un
meccanismo di Validazione attraverso le pipes direttamente sul metodo che si
occupa di gestire la richiesta

4.2     Caso di studio e implementazione di un Web

        Server RESTful

4.2.1     Caso di studio

Ci proponiamo di implementare un web server che faccia da interfaccia API al sito
docenti.unina.it. L’attuale sito è stato recentemente ristrutturato utilizzando

                                        32
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

Angular, trasformandolo quindi in una vera e proprio Single Page Application,
contenuta in unico script compilato, eseguita dal browser e cachata per successivi
utilizzi. In Angular è definita la struttura e lo stile dell’app; i dati invece, li
prende attraverso richieste AJAX dal server unina.it. Dunque abbiamo la web
app - il frontend - già pronto, cosa possiamo fare? Ci proporremo di implementare
un’interfaccia API REST con Node.js e NestJS, che implementi semplicemente
le seguenti azioni:

   • Lista dei docenti

   • Visualizzazione di un docente

   • Prenotazione di un esame

4.2.2     Interfaccia REST

Possiamo da subito intuire che le tre azioni possono essere facilmente tradotte
nel paradigma REST. Consideriamo il docente, la prenotazione l’esame come
risorse e l’azione su di essi come verbo della richiesta, in particolare:

   • GET /docenti - Ottiene la lista dei docenti

   • GET /docenti/id - Ottiene il docente con identificativo id

   • POST /prenotazioni - Effettua una prenotazione su un dato esame

   Osserviamo come nella POST la prenotazione si riferisce sempre ad un esame
ma la risorsa non è presenta nella struttura dell’endpoint. Infatti l’identificativo
dell’esame è un parametro che andremo a inserire nel body della richiesta HTTP.
Il nome del parametro e la sua forma sono vincoli che deve imporre l’interfaccia
API attraverso una documentazione. Questa è più una scelta sintattica che seman-
tica , poiché REST propone anche una struttura con sotto-risorse che si traduce
in sotto-path nell’endpoint; in particolare potremmo implementare la POST in

                                         33
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

questo modo: POST /esami/idEsame/prenotazioni dove idEsame sarà il para-
metro dell’identificativo dell’esame, che inseriremo quindi nella query dell’URL
invece che nel body. Il risultato non cambia, cambia solo il modo di comunicare
tra le due parti.

4.2.3     Autenticazione e Autorizzazione

Dato che la prenotazione di un esame ha bisogno di un utente loggato nel sistema
per identificare lo studente che effettua la prenotazione, dovremmo implementa-
re un meccanismo di autenticazione e autorizzazione e potremmo pensare di
usare un’autenticazione Token-Based con una JWT (JsonWebToken) e un
middleware in Nest che controlli che il client includa questa token nell’header
e se questa sia valida e non scaduta. Brevemente, la JWT è una token generata
dal server: è un oggetto JSON che contiene informazioni riguardo la scadenza
della token, il soggetto che l’ha creata e altre informazioni utili al client; il tutto
viene codificato in una stringa ASCII. Al momento dell’autenticazione il ser-
ver la genera e viene restituita al client, che provvede a salvarla localmente (nel
window.localStorage ad esempio). Quando il client deve effettuare una richiesta
autorizzata, inserirà la token nell’header Authorization. Il server provvederà
a leggere questo header e a convalidate la token: essa viene decodificata e il
server può leggere le informazioni circa la scadenza e l’id dell’utente. Se la token è
non valida, scaduta o l’utente associato non esiste imiddleware restituirà subito un
errore di 401 Unauthorized al client. Se tutto va bene, il middleware inserisce
nell’oggetto request le informazioni sull’utente loggato e la richiesta procede con
il successivo middleware o direttamente con l’handler.
   Per semplicità, nel progetto in appendice non implementeremo i metodi di
Autenticazione e Autorizzazione. Supporremo cioè che il client abbia già effettuato
l’accesso e tenga memorizzata la JWT nello storage e che l’AuthMiddleware abbia

                                          34
CAPITOLO 4. NESTJS - PRINCÌPI ED APPLICAZIONI

già processato la richiesta.

4.2.4     Approccio Controller - Service

Dato che sono tecniche che NestJS supporta out-of-the-box, le implementeremo
molto velocemente. In particolare il Controller si occuperà di ricevere la richie-
sta e interrogare il Service per ottenere la lista di risorse nel caso delle GET
o crearne una nuova nel caso della POST. Il service si occuperà di interrogare
una eventuale Repository collegata all’interfaccia del database. Dato che sempre
per semplicità non abbiamo parlato di database (la scelta più veloce e semplice è
quella di usare MongoDB come database), ci fermeremo all’implementazione del
service, presupponendo quindi che già ci sia un livello dati implementato con
un’interfaccia Repository e dei Model definiti (i model in questo caso sono quelli
relativi a Docente, Prenotazione, Esame).
   Questo dunque è quello che ci limiteremo a fare, tralasciando molte cose es-
senziali per avere una struttura completa ma avendo utilizzando comunque molti
dei concetti e tecniche studiate durante questo elaborato. Il lettore che è inte-
ressato all’implementazione via codice di questo caso di studio, faccia riferimento
all’Appendice A.

                                        35
Appendices

    36
Appendice A

Implementazione Web Server
RESTful in NestJS

Requisito fondamentale per creare un progetto NestJS è avere ovviamente Node.js
installato sulla proprio macchina. Per l’installazione di Node.js e NPM si segua la
procedura all’indirizzo https://nodejs.org/it/.
   Una volta installato Node.js bisognerà installare la CLI di Nest, che permette
di creare un progetto Nest direttamente da terminale. Apriamo il terminale e
digitiamo e seguiamo le istruzioni a video.

         Figura A.1: Installazione Nest CLI e creazione nuovo progetto

   Una volta creato il progetto, apriamolo con un IDE che supporti TypeScript
(Atom, VSCode, WebStorm...) e potremo visualizzare la struttura dei file già
impostata da Nest. Particolare importanza hanno:

   • Il package.json, un file di metadati dove sono salvate tutte le informazioni
     riguardo le dipendenze di terze parti gestite da NPM.

                                        37
APPENDICE A. IMPLEMENTAZIONE WEB SERVER RESTFUL IN
                                                    NESTJS

   • La cartalla node_modules, gestita sempre da NPM e creata seguendo le
     direttive contenute nel package.json

   • tslint.json, tsconfig.json, file di configurazione per la compilazione di Type-
     Script e della convalida del codice

   • La cartella src, il cuore del nostro progetto dove andremo a creare i file di
     codice che ci serviranno

   Per avviare il nostro server, basterà digitare il comando sul terminale: npm
run start
   Notiamo come Nest ci crei il root module app e un relativo app.service e
app.controller.

Uso dei Decorator

Aprendo il file app.controller, notiamo subito la direttiva @Controller : questo è
un decorator è una delle funzionalità aggiunte da TypeScript a ES6. Sono delle
funzioni che possono modificare o arricchire una classe, un metodo o un argomen-
to andando a modificare i cosidetti metadati [1]. Nest in particolare fa un uso
fondamentale dei decorators, che permettono di modularizzare il codice e renderlo
più pulito. Risulta subito chiaro allora che il decorator @Controller, importato
direttamente da Nest, aggiunge le necessarie funzionalità alla classe AppControl-
ler affinché sia configurabile appunto come un controller. Usando questa direttiva
Nest mapperà questa classe nella lista dei Controller e saprà che il nome del
controller corrisponde al nome della route: dunque se il nostro controller
si chiama FooController, l’endpoint associato sarà /foo. Se vogliamo che il nome
della route sia diverso dal nome del controller, basterà specificarlo tra le paren-
tesi di @Controller. Nel caso dell’app.controller, dato che AppModule è il root
module, il controller sarà mappato sulla route di base, cioè / Allo stesso modo
funziona, sempre vedendo il file app.controller la direttiva @Get: questa indica a

                                        38
APPENDICE A. IMPLEMENTAZIONE WEB SERVER RESTFUL IN
                                                    NESTJS

Nest che il metodo è l’handler della richiesta GET sull’endpoint GET /. Infat-
ti, una volta avviato il server, se navighiamo all’indirizzo http://localhost:3000/
notiamo che viene visualizzato un messaggio "Hello World" che non è altro che
la risposta impostata dal metodo AppController@getHello, che chiama il servizio
corrispondente che restituisce la stringa.

Service Injection

Un’ultima cosa da osservare prima di proseguire con il nostro progetto è la fun-
zione contructor di AppController: il parametro definito come private readonly
appService: AppService indica a Nest che nella variabile appService va iniettata
l’istanza del servizio AppService attraverso la dependency injection. Le direttiva
private readonly sono chiavi riservate di TypeScript che indicano che la variabile
è privata ed è di sola lettura. Il service, il controller e la loro dipendenza vengono
instanziati tutti in AppModule in maniera dichiarativa.

Moduli personalizzati

Creiamo i nostri moduli, EsameModule e DocenteModule con i relativi controller
e service e configuriamoli in maniera analoga all’AppModule. Successivamente
inseriamo i due moduli nel campo imports di AppModule in modo da indicare a
Nest i sotto moduli del root module e costuire così l’application graph
   Seguendo le stesse direttive vista prima e i concetti affrontati nel capitolo 4, il
controller e il service per il Docente possono essere implementati come in figura.
Navigando su http://localhost:3000/docenti si potrà vedere la rappresenta-
zione della lista della risorsa docente in formato JSON (scelto di default da
NestJS), che altro non è che la trasformazione dell’Array di docenti fakeDocenti
che abbiamo definito nel service per simulare il dato nel database.
   Allo stesso, modo implementiamo PrenotazioneController e PrenotazioneSer-
vice, stavolta usando il decorator @Post per indicare il metodo come POST.

                                         39
APPENDICE A. IMPLEMENTAZIONE WEB SERVER RESTFUL IN
                                                    NESTJS

   Analogamente a prima, effettuando una POST (si può usare il tool Postman o
stesso la linea di comando curl ) e inserendo nel body il campo id_esame, il server
risponderà con un oggetto prenotazione con i dati relativi all’esame prenotato, allo
studente che ha prenotato e all’istante di prenotazione.

Riassumendo

Questa ovviamente è solo una piccola anteprima di un progetto ben più ampio,
ma dimostra coma sia semplice, intuitivo e veloce fare il setup di un progetto con
solide basi architetturali, utilizzando concetti come Moduli, Dependency Injection,
Decorators, Service, Controller, più tutti gli altri che abbiamo discusso come Inter-
ceptors, Middleware, Pipes facilmente implementabili seguendo la documentazione
su docs.nestjs.com.

                                         40
APPENDICE A. IMPLEMENTAZIONE WEB SERVER RESTFUL IN
                                            NESTJS

           (a) Implementazione di DocenteController

            (b) Implementazione di DocenteService

                               41

    (c) Aggiunta del Controller e del Service al root module
Puoi anche leggere