Come costruire un API Gateway con Express
Hai bisogno di sapere come costruire un API Gateway con Express, ma non sai come fare? Nessun problema.
Chiariamo il concetto di API gateway e poi vediamo come realizzarne uno, oppure quali tra gli strumenti già esistenti è possibile (e consigliato usare).
Cos’è un API gateway
Funzionamento dell’API gateway con 2+ microservizi
Un API gateway è un componente che accetta tutte le request API dai client e quindi le instrada al microservizio appropriato attraverso il routing, la composizione e il protocollo appropriato. In altre parole, è un servizio che permette di gestire più richieste a servizi diversi, occupandosi di prendere in carico le chiamate e “smistarle” al microservizio giusto, determinando il percorso migliore.
Può occuparsi anche della traduzione tra protocolli Web e protocolli non compatibili con l’applicazione del client (come AMQP o WebSocket).
Inoltre, l’API gateway è un vero e proprio pattern: è consigliato se si desidera progettare e creare applicazioni basate su microservizi complessi o di grandi dimensioni con più applicazioni client che devono accedere ad una serie di servizi. Lo scopo è simile a quello del pattern cosiddetto facade: in questo caso però, costituisce un proxy inverso all’interno di un sistema e utilizza un modello di comunicazione sincrono.
Un API gateway fornisce un singolo endpoint o URL che le applicazioni client possono utilizzare e mappa internamente le richieste ai microservizi appropriati; questo fornisce quindi un livello di astrazione maggiore, nascondendo alcuni dettagli di implementazione (ad esempio, il modo in cui la richiesta verrà gestita), oltre a funzionalità aggiuntive come piani di sottoscrizione (chi può interrogare una serie di servizi e fino a quando) o trasformazioni dell’input e dell’output, o ancora gestione dei logs e degli errori.
In questo articolo, il nostro API gateway fungerà semplicemente da gateway tra i client e i nostri servizi.
Tutorial
Creazione del progetto Express Gateway
Per creare un progetto con Express Gateway, è necessario installare Node.js ed Express Gateway; una volta fatto, è possibile usare questo comando per creare un progetto che ci permetta di scrivere la nostra applicazione:
$ eg gateway create
Verrà generato un progetto con i seguenti file:
File generati dal comando precedente
Chiaramente, dovremo avere a disposizione uno (o più) servizi con il quale il nostro gateway dovrà interagire. All’interno di questo articolo, andremo ad utilizzare questo repository, che offre un servizio di memorizzazione delle attività di una TODO list, ma nulla esclude che il nostro gateway possa in futuro aggiungere altri servizi e fornire un unico punto di accesso!
Configurazione del gateway
Per creare dei servizi manutenibili, esiste il file gateway.config.yml, che permette di definire un elenco di API che il gateway deve andare ad interrogare quando riceve una richiesta da un client.
Andiamo a modificare le seguenti voci: per prima cosa, definiamo nella riga 6 all’interno della proprietà apiEndpoints una nuova API che il nostro gateway andrà ad esporre, ossia tasksApi: a partire dall’indirizzo localhost (il nostro gateway si trova in locale, al momento), mapperà tutti i path sotto /tasks* (vedere la definizione all’interno del file di routing del singolo microservizio), sfruttando il servizio che andiamo a definire subito dopo, all’interno di serviceEndpoints:
apiEndpoints:
tasksApi:
host: localhost
paths: '/tasks\*'
serviceEndpoints:
tasksService:
url: 'http://localhost:8081/api'
Anche i servizi sono esposti su localhost, ma sulla porta 8081: nel caso del gateway, andremo ad utilizzare la porta 8080, definendo questa regola all’inizio del file:
http:
port: 8080
L’oggetto pipelines ci permette di definire in che modo il gateway deve cablare gli endpoint dell’API e il servizio: in questo caso, ne creiamo una ad hoc chiamata tasks che utilizza l’API endpoint definito in precedenza e stabilisce a quale servizio deve fare riferimento quando riceve una request:
pipelines:
- name: tasks
apiEndpoints:
- tasksApi
policies:
- proxy:
- action:
serviceEndpoint: tasksService
changeOrigin: true
Testiamo su Postman
Per assicurarci che la nostra API funzioni correttamente, possiamo testarla con Postman (o qualunque altro strumento).
Quello che avremmo fatto in precedenza, utilizzando il microservizio, sarebbe stato di effettuare una chiamata come quella riportata in figura, dove interroghiamo direttamente il servizio attraverso la sua porta:
Chiamata al microservizio
Invece, adesso disponiamo di un gateway che ci permette di reindirizzare correttamente le request, utilizzando un’unica porta (la 8080):
Chiamata al microservizio attraverso il gateway
Abbiamo raggiunto il nostro obiettivo configurando un solo microservizio: se ne volessimo aggiungere altri 10? Sarebbe sufficiente modificare il file seguendo gli step visti in precedenza per ogni nuovo microservizio, ed il gioco è fatto!
Definire un customer API
Express Gateway offre anche la possibilità di definire dei customer, ossia degli utenti che sono autorizzati ad utilizzare l’API, e questo ci permette di mettere in sicurezza i nostri servizi.
Per creare un nuovo consumer, eseguiamo la seguente request sulla porta 9876 (che viene utilizzata per la gestione amministrativa):
Creazione di un consumer
Una volta inserite le informazioni, dobbiamo specificare che la nostra API ha bisogno di una chiave di autenticazione, e lo facciamo in questo modo: inseriamo all’interno dell’attributo policies la proprietà key-auth, che ci permette di definire che l’API sarà esposta tramite una chiave di autenticazione con un header personalizzato (opzionale) che definiamo come CUSTOM-HEADER:
pipelines:
- name: tasks
apiEndpoints:
- tasksApi
policies:
- key-auth:
- action:
apiKeyHeader: CUSTOM-HEADER
disableHeadersScheme: true
- proxy:
- action:
serviceEndpoint: tasksService
changeOrigin: true
A questo punto, creiamo delle credenziali da assegnare all’utente creato poco fa con il seguente comando:
Creazione credenziali nel formato key-auth
Se proviamo a eseguire nuovamente una request lasciando le impostazioni così com’erano, questo è quello che otteniamo:
La response è (giustamente) unauthorized
Per far sì che funzioni, andiamo ad aggiungere un header alla nostra request, dove il nome sarà quello configurato in precedenza, e il valore sarà dato dalla keyId:keySecret, ossia le due chiavi che abbiamo ottenuto durante il processo di creazione:
Aggiunta dell’header alla request
Alternative
Se ti è rimasta della curiosità, prova a dare un’occhiata a queste alternative: