RESTFul API con node.js ed express.js – Parte 3

API scalabile node ed express  - nodeScalabile - RESTFul API con node.js ed express.js – Parte 3

Dopo la seconda parte dell’articolo, sono rimasto in debito della conclusione, che andrò a descrivere in questa terza parte.

In questa terza parte, introduciamo l’utilizzo delle classi nel routing delle chiamate.

Utilizzo delle classi nel routing

Nel callback delle funzioni di routing, sostituiamo la chiamata diretta con il riferimento al metodo della classe. Ma andiamo con ordine.

Partiamo creando all’interno del nostro progetto una sottocartella col nome della classe, al quale aggiungiamo il suffisso “Controllers”. Per esempio se vogliamo scrivere le funzioni che gestiscono la lista delle attività da fare (todo) procediamo così:

mkdir todosControllers
nano todosControllers/todos.js

Nel file scriviamo:

class TodosController {
     sayHello(req, res) {
        return res.status(200).send({
           success: 'true',
           message: 'Hello World!'
        });
      }
}
const todoController = new TodosController();
module.exports = todoController;

La classe TodosController presenta il metodo sayHello che restituisce il messaggio “Hello World!” Inoltre le ultime due righe permettono di attivare la classe ed esportare il modulo per poter essere utilizzato nel file del routing (o dovunque possa servire).

A questo punto nel file routes/index.js andiamo a sostituire nel routing il callback con la funzione della classe todosControllers, non prima però di aver importato la classe stessa nel file:

const todoController = require('../todosController/todos.js');

[..]

routing.get('/', todoController.sayHello);

Come si può vedere, la chiamate GET del routing risulta molto più pulita e semplice da leggere. Inoltre questo ci permette di scrivere metodi complessi all’interno delle classi.

Questo primo trittico di articoli, vuole essere un’introduzione alla scrittura delle API RESTFul. Vorrei arricchire la collezione di articoli sulle API RESTFul trattando argomenti come gli Swagger per la generazione automatica della documentazione e di un ambiente di test, la limitazione delle API e l’autenticazione, solo per citarne alcuni.

RESTFul API con node.js ed express.js – Parte 2

API scalabile node ed express  - nodeScalabile - RESTFul API con node.js ed express.js – Parte 2

Nella prima parte dell’articolo RESTFul API con node.js ed express.js – Parte 1 abbiamo visto l’approccio per creare un servizio RESTFul con node.js ed express.js, dall’installazione delle librerie, al classico Hello World!

Oggi vediamo come migliorare la struttura delle nostre API in modo da renderla scalabile. Aggiungiamo un Middleware per ottimizzare i flussi di lavoro, per rendere più ordinato il codice ma sopratutto per renderlo scalabile.

Middleware

Il middleware è un insieme di funzioni che hanno accesso agli oggetti richiesta e risposta e vengono richiamate per l’esecuzione di specifiche operazioni;

Creiamo il Middleware per gestire il routing delle API, spostandolo da punto di accesso (app.js) dell’app.
All’interno della cartella del progetto, creo una sottocartella chiamata routes e al suo interno creo il file index.js, nel quale scrivo:

import express from 'express';

const router = express.Router();

Ho creato un gestore per il routing. Ora sposto dal file app.js a questo file il routing ed esporto questo gestore per poter essere utilizzato nell’intero progetto:

routing.get('/', function (req, res) {
   res.status(200).send({
      success: 'true',
      message: 'Hello World!'
   })
});
module.exports = router;

allo stesso modo nel file app.js, od ovunque mi serva, devo importare il gestore e usarlo nell’app:

const router = require('./routes/index.js');

app.use(router);

In questo modo si può vedere come risulti molto pulito il codice del file app.js:

const express   = require('express');
const bodyParse = require('body-parser');
const router    = require('./routes/routes.js');

const app  = express();
const PORT = 5000;

//Parse incoming request
app.use(bodyParse.json());
app.use(bodyParse.urlencoded({extended: false}));
app.use(router);


app.listen(PORT, () => {
    console.log(`server running on port ${PORT}`)
});

sfruttando le caratteristiche del Middleware otteniamo un punto di accesso chiaro e pulito, senza eccessive necessità di modifiche e possibilità di implementare il codice in modo ordinato.

const express = require('express');

const router = express.Router();

routing.get('/', function (req, res) {
  res.status(200).send({
    success: 'true',
    message: 'Hello World!'
  })
});
//Export Module
module.exports = router;

Nella prossima parte, andremo a vedere come implementare delle classi e le relative funzioni per distribuire e ottimizzare ulteriormente il codice. Tutto questo per arrivare ad avere una struttura altamente scalabile e replicabile per tutti i nostri progetti.

RESTFul API con node.js ed express.js – Parte 1

RESTFul API Node.js  - RESTfulAPIs NodeJS - RESTFul API con node.js ed express.js – Parte 1

Le basi

Creo la cartella che contiene il progetto e creo il file app.js punto di origine dell’applicazione REST API.
Inizializzo il gestore di pacchetti npm col comando npm init, questo mi crea il file package.json che memorizza tutte le dipendenze delle librerie installate con la versione.
Installo la prima libreria, necessaria per la creazione del servizio:

npm install express --save

all’interno del file app.js scrivo il primo esempio di codice per testare il funzionamento:

const express = require('express');

// Set up the express app
const app = express();

app.get('/', function (req, res) {
  res.status(200).send({
    success: 'true',
    message: 'Hello World!'
  })
});
const PORT = 5000;

app.listen(PORT, () => {
  console.log(`server running on port ${PORT}`)
});

Per poter inviare informazioni in formato JSON alle API, è necessario installare la libreria body-parse:

npm install body-parser --save

importarla all’interno del progetto (app.js) e configurarla:

const body-parser = require('body-parser');

// Parse incoming requests data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

a questo punto le funzioni possono intercettare i valori passati nel body in formato JSON:

if(!req.body.title) {
   return res.status(400).send({
      success: 'false',
      message: 'title is required'
   });
}

questo per esempio verifica la presenza della chiave title all’interno del JSON e se non presente restituisce messaggio di errore.

Nel prossimo articolo vedremo come costruire un Middleware per gestire il routing delle chiamate alle API.

RESTFul API con node.js ed express.js – Parte 2

Handlebars e Nodejs su AWS Lambda

Programming code on a monitor.  - coding - Handlebars e Nodejs su AWS Lambda

Helpers di Handlebars in Nodejs per template complessi

Mi sono ritrovato a dover creare delle pagine html statiche recuperando i dati da un database tramite la creazione di una funzione Lambda su AWS. La soluzione che mi è parsa subito la migliore è stata quella di scegliere di utilizzare la libreria https://handlebarsjs.com/ che permette la creazione di un template e la sostituzione all’interno di singoli riferimenti o di parti più complesse.

Non avendo mai utilizzato gli helpers di Handlebars, ho iniziato la ricerca online per trovare qualche suggerimento. Di seguito riporto i passaggi seguiti per l’utilizzo all’interno di una funzione Lambda, messi insieme sfruttando quanto trovato online e dopo alcuni ragionamenti personali.

Necessità:

  • scrivere una funziona async/wait che recuperato un template, sostituisca tutte le occorrenze e restituisca un html usabile per essere salvato in un file html su S3
  • utilizzare gli helpers per la sostituzione di occorrenze complesse, sia da dati statici (file json) sia da dati dinamici recuperati da database

Alla funzione, richiamata in modo asincrono, vengono passati i dati dinamici recuperati dal database. All’interno di un blocco try/catch richiama gli helpers e il template elabora i dati e restituisce un html pronto ad essere salvato su S3 per essere pubblicato online.

const document = fs.readFileSync('template-tpl.html', 'utf-8');
const menuJson = require("menu.json");
async function getHtmlPageContent(pack){
    try {
        require("hb-helpers.js")(handlebars, pack);
        const template = handlebars.compile(document);
        let data = {};
        data.itemsMenu = menuJson;
        const html = template(data);
        return html;
    } catch (error) {
        console.log('HBS ERROR', error);
        return context.fail(error);
    }
}

Fuori dalla funzione, in fase di impostazione, avevo dichiarato due costanti: la prima contenente il template html, la seconda contenente la struttura json del menù. Per prima cosa nella funzione, richiamo un file js esterno contenente gli helpers:

require("hb-helpers.js")(handlebars, pack);

e gli passo l’oggetto handlebars (oggetto della libreria) e i valori dinamici ottenuti dal database utilizzati nell’helpers per la gestione delle lingue. Quindi compilo il template html nella costante template e imposto la variabile itemsMenu dell’oggetto data con i valori del json per costruire il menu.

const template = handlebars.compile(document);
let data = {};
data.itemsMenu = menuJson;

A questo punto fondo l’oggetto data con il template per ottenere l’html completo che la funzione restituirà.

const html = template(data);
return html;

Successivamente analizziamo nel dettaglio alcuni particolari dei singoli file utilizzati.

Template HTML – (template-tpl.html)

<div class="navbar-collapse collapse float-right nav-main-collapse">
   <nav class="nav-main">
      <ul id="topMain" class="nav nav-pills nav-main">
          {{#each itemsMenu}}
              {{menuItem}}
          {{/each}}
      </ul>
  </nav>
</div>

Blocco html del menu contenente i tag di Handlebars per iterare sull’oggetto itemsMenu (variabile dell’oggetto data contenente il json), dove menuItem è l’oggetto che verrà utilizzato dall’helpers per la sostituzione con le singole voci di menu.

Helpers – (hb-helpers.js)

function hbsHelpers(hbs, pack) {
   //MENU
   hbs.registerHelper('menuItem', function() {
     var text    = hbs.escapeExpression(this.text),
         liClass = hbs.escapeExpression(this.liClass),
         href    = hbs.escapeExpression(this.url),
         aClass  = hbs.escapeExpression(this.aClass),
         spanClass  = hbs.escapeExpression(this.spanClass);
     return new hbs.SafeString(
       '<li class="'+liClass+'"><a href="'+href+'" class="'+aClass+'"><span class="trn '+spanClass+'">'+text+'</span></a></li>'
     );
   });
}
module.exports = hbsHelpers;

La funzione hbsHelpers con la registrazione dell’helpers menuItem. Questo helper recupera dal json (oggetto data.itemsMenu) i valori per text, liClass, href, aClass e spanClass e li utilizza per la costruzione della voce di menu.

Json del menu – (menu.json)

[
    {
        "text": "HOME",
        "icon": "",
        "liClass": "",
        "aClass": "",
        "spanClass": "theme-color",
        "url": "/index.html"
    },
    {
        "text": "TOUR",
        "icon": "",
        "liClass": "",
        "aClass": "",
        "spanClass": "theme-color",
        "url": "/tour.html"
    }
]

Un semplicissimo json contenente i valori per la costruzione del menu.

In conclusione, sfruttando le potenzialità di Handlebars e dei suoi helpers, è possibile costruire un html partendo da un template complesso.