Let LoopBack Do It: una procedura dettagliata del framework dell'API Node che hai sempre sognato
Pubblicato: 2022-03-11Inutile menzionare la crescente popolarità di Node.js per lo sviluppo di applicazioni. eBay gestisce un servizio API Node di produzione dal 2011. PayPal sta ricostruendo attivamente il proprio front-end in Node. Il sito mobile di Walmart è diventato la più grande applicazione Node, dal punto di vista del traffico. Nel weekend del Ringraziamento del 2014, i server Walmart hanno elaborato 1,5 miliardi di richieste, il 70% delle quali è stato consegnato tramite dispositivi mobili e basato su Node.js. Per quanto riguarda lo sviluppo, il Node Package Manager (npm) continua a crescere rapidamente, superando di recente i 150.000 moduli ospitati.
Mentre Ruby ha Rails e Python ha Django, il framework di sviluppo delle applicazioni dominante per Node deve ancora essere stabilito. Ma c'è un potente contendente che sta guadagnando terreno: LoopBack, un framework API open source creato da San Mateo, California, società StrongLoop. StrongLoop è un importante contributo all'ultima versione di Node, per non parlare dei manutentori di lunga data di Express, uno dei framework Node più popolari esistenti.
Diamo un'occhiata più da vicino a LoopBack e alle sue capacità trasformando tutto in pratica e creando un'applicazione di esempio.
Cos'è LoopBack e come funziona con Node?
LoopBack è un framework per creare API e connetterle con origini dati back-end. Basato su Express, può prendere una definizione di modello di dati e generare facilmente un'API REST end-to-end completamente funzionale che può essere chiamata da qualsiasi client.
LoopBack viene fornito con un client integrato, API Explorer . Lo useremo poiché rende più facile vedere i risultati del nostro lavoro e in modo che il nostro esempio possa concentrarsi sulla creazione dell'API stessa.
Ovviamente avrai bisogno di Node installato sulla tua macchina per seguire. Prendilo qui. npm viene fornito con esso, quindi puoi installare facilmente i pacchetti necessari. Iniziamo.
Crea uno scheletro
La nostra applicazione gestirà le persone che vorrebbero donare regali, o cose di cui non hanno più bisogno, a qualcuno che potrebbe averne bisogno. Quindi, gli utenti saranno Donatori e Destinatari. Un donatore può creare un nuovo regalo e vedere l'elenco dei regali. Un Destinatario può vedere l'elenco dei regali di tutti gli utenti e può rivendicare quelli che non sono stati reclamati. Ovviamente, potremmo costruire Donatori e Destinatari come ruoli separati sulla stessa entità (Utente), ma proviamo a separarli in modo da poter vedere come costruire relazioni in LoopBack. Il nome di questa innovativa applicazione sarà Givesomebody .
Installa gli strumenti della riga di comando StrongLoop tramite npm:
$ npm install -g strongloop
Quindi esegui il generatore di applicazioni di LoopBack:
$ slc loopback _-----_ | | .--------------------------. |--(o)--| | Let's create a LoopBack | `--------- | application! | ( _U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ` |° Y ` ? What's the name of your application? Givesomebody
Aggiungiamo un modello. Il nostro primo modello si chiamerà Gift. LoopBack chiederà l'origine dati e la classe base. Dal momento che non abbiamo ancora impostato l'origine dati, possiamo inserire db (memory)
. La classe base è una classe modello generata automaticamente e in questo caso vogliamo utilizzare PersistedModel
, poiché contiene già tutti i soliti metodi CRUD per noi. Successivamente, LoopBack chiede se deve esporre il modello tramite REST (sì) e il nome del servizio REST. Premi invio qui per utilizzare il valore predefinito, che è semplicemente il plurale del nome del modello (nel nostro caso, gifts
).
$ slc loopback:model ? Enter the model name: Gift ? Select the data-source to attach Gift to: (Use arrow keys) ❯ db (memory) ? Select model's base class: (Use arrow keys) Model ❯ PersistedModel ? Expose Gift via the REST API? (Y/n) Yes ? Custom plural form (used to build REST URL):
Infine, diamo i nomi delle proprietà, i loro tipi di dati e i flag obbligatori/non richiesti. Il regalo avrà proprietà di name
e description
:
Let's add some Gift properties now. Enter an empty property name when done. ? Property name: name invoke loopback:property ? Property type: (Use arrow keys) ❯ string ? Required? (y/N)Yes
Immettere un nome di proprietà vuoto per indicare che hai finito di definire le proprietà.
Il generatore di modelli creerà due file che definiscono il modello in common/models
dell'applicazione: gift.json
e gift.js
. Il file JSON specifica tutti i metadati sull'entità: proprietà, relazioni, convalide, ruoli e nomi di metodi. Il file JavaScript viene utilizzato per definire un comportamento aggiuntivo e per specificare gli hook remoti da chiamare prima o dopo determinate operazioni (ad esempio, creare, aggiornare o eliminare).
Le altre due entità modello saranno i nostri modelli Donatore e Ricevitore. Possiamo crearli usando lo stesso processo, tranne che questa volta mettiamo User
come classe base. Ci darà alcune proprietà come username
, password
, email
fuori dagli schemi. Possiamo aggiungere solo il nome e il paese, ad esempio, per avere un'entità completa. Per il Destinatario vogliamo aggiungere anche l'indirizzo di consegna.
Struttura del progetto
Diamo un'occhiata alla struttura del progetto generato:
Le tre directory principali sono: - /server
– Contiene gli script dell'applicazione del nodo ei file di configurazione. - /client
– Contiene .js, .html, .css e tutti gli altri file statici. - /common
– Questa cartella è comune sia al server che al client. I file del modello vanno qui.
Ecco una ripartizione dettagliata dei contenuti di ciascuna directory, presa dalla documentazione di LoopBack:
File o directory | Descrizione | Come accedere in codice |
---|---|---|
Directory dell'applicazione di primo livello | ||
package.json | Specifiche del pacchetto npm standard. Vedi package.json | N / A |
/server directory - File dell'applicazione del nodo | ||
server.js | File principale del programma applicativo. | N / A |
config.json | Impostazioni dell'applicazione. Vedi config.json. | app.get('setting-name') |
datasources.json | File di configurazione dell'origine dati. Vedi datasources.json. Per un esempio, vedere Creare una nuova origine dati . | app.datasources['datasource-name'] |
model-config.json | File di configurazione del modello. Vedi model-config.json. Per ulteriori informazioni, vedere Collegamento dei modelli alle origini dati . | N / A |
middleware.json | File di definizione del middleware. Per ulteriori informazioni, vedere Definizione del middleware. | N / A |
/boot | Aggiungi script per eseguire l'inizializzazione e la configurazione. Vedi script di avvio. | Gli script vengono eseguiti automaticamente in ordine alfabetico. |
/ directory client - file dell'applicazione client | ||
README.md | I generatori di loopback creano un file README vuoto in formato markdown. | N / A |
Altro | Aggiungi i tuoi file HTML, CSS, JavaScript del client. | |
/directory comune - file dell'applicazione condivisi | ||
/models | File di modello personalizzati:
| Nodo:myModel = app.models.myModelName |
Costruisci relazioni
Nel nostro esempio, abbiamo alcune relazioni importanti da modellare. Un donatore può donare molti doni, il che dà alla relazione il donatore ha molti doni . Un destinatario può anche ricevere molti regali, quindi abbiamo anche la relazione che riceve molti regali . D'altra parte, il Regalo appartiene al Donatore e può anche appartenere al Ricevitore se il Ricevitore sceglie di accettarlo. Mettiamolo nel linguaggio di LoopBack.
$ slc loopback:relation ? Select the model to create the relationship from: Donor ? Relation type: has many ? Choose a model to create a relationship with: Gift ? Enter the property name for the relation: gifts ? Optionally enter a custom foreign key: ? Require a through model? No
Si noti che non esiste un modello passante; stiamo solo tenendo il riferimento al Dono.
Se ripetiamo la procedura precedente per Ricevitore e aggiungiamo due appartenenze alle relazioni con Regalo, realizzeremo il nostro modello di progettazione su un lato posteriore. LoopBack aggiorna automaticamente i file JSON affinché i modelli esprimano esattamente ciò che abbiamo appena fatto attraverso queste semplici finestre di dialogo:
// common/models/donor.json ... "relations": { "gifts": { "type": "hasMany", "model": "Gift", "foreignKey": "" } }, ...
Aggiungi un'origine dati
Ora vediamo come allegare un'origine dati reale per archiviare tutti i dati della nostra applicazione. Ai fini di questo esempio, utilizzeremo MongoDB, ma LoopBack ha moduli per la connessione con Oracle, MySQL, PostgreSQL, Redis e SQL Server.
Innanzitutto, installa il connettore:
$ npm install --save loopback-connector-mongodb
Quindi, aggiungi un'origine dati al tuo progetto:
$ slc loopback:datasource ? Enter the data-source name: givesomebody ? Select the connector for givesomebody: MongoDB (supported by StrongLoop)
Il passaggio successivo consiste nel configurare l'origine dati in server/datasources.json
. Utilizzare questa configurazione per un server MongoDB locale:
... "givesomebody": { "name": "givesomebody", "connector": "mongodb", "host": "localhost", "port": 27017, "database": "givesomebody", "username": "", "password": "" } ...
Infine, apri server/model-config.json
e cambia l'origine datasource
per tutte le entità che vogliamo mantenere nel database in "givesomebody"
.
{ ... "User": { "dataSource": "givesomebody" }, "AccessToken": { "dataSource": "givesomebody", "public": false }, "ACL": { "dataSource": "givesomebody", "public": false }, "RoleMapping": { "dataSource": "givesomebody", "public": false }, "Role": { "dataSource": "givesomebody", "public": false }, "Gift": { "dataSource": "givesomebody", "public": true }, "Donor": { "dataSource": "givesomebody", "public": true }, "Receiver": { "dataSource": "givesomebody", "public": true } }
Testare la tua API REST
È ora di vedere cosa abbiamo costruito finora! Useremo il fantastico strumento integrato, API Explorer , che può essere utilizzato come client per il servizio che abbiamo appena creato. Proviamo a testare le chiamate API REST.
In una finestra separata, avvia MongoDB con:
$ mongod
Eseguire l'applicazione con:
$ node .
Nel tuo browser, vai a http://localhost:3000/explorer/
. Puoi vedere le tue entità con l'elenco delle operazioni disponibili. Prova ad aggiungere un donatore con una chiamata POST /Donors
.
API Explorer è molto intuitivo; selezionare uno dei metodi esposti e lo schema del modello corrispondente verrà visualizzato nell'angolo in basso a destra. Nell'area di testo dei data
è possibile scrivere una richiesta HTTP personalizzata. Una volta compilata la richiesta, fai clic sul pulsante "Prova" e la risposta del server verrà visualizzata di seguito.

Autenticazione utente
Come accennato in precedenza, una delle entità precompilate con LoopBack è la classe User. L'utente possiede metodi di accesso e disconnessione e può essere associato a un'entità AccessToken che conserva il token dell'utente specifico. In effetti, un sistema completo di autenticazione dell'utente è pronto per uscire dagli schemi. Se proviamo a chiamare /Donors/login
tramite API Explorer , ecco la risposta che otteniamo:
{ "id": "9Kvp4zc0rTrH7IMMeRGwTNc6IqNxpVfv7D17DEcHHsgcAf9Z36A3CnPpZJ1iGrMS", "ttl": 1209600, "created": "2015-05-26T01:24:41.561Z", "userId": "" }
L' id
è in realtà il valore dell'AccessToken, generato e mantenuto automaticamente nel database. Come vedete qui, è possibile impostare un token di accesso e utilizzarlo per ogni richiesta successiva.
Metodi remoti
Un metodo remoto è un metodo statico di un modello, esposto su un endpoint REST personalizzato. I metodi remoti possono essere usati per eseguire operazioni non fornite dall'API REST del modello standard di LoopBack.
Oltre ai metodi CRUD che otteniamo fuori dagli schemi, possiamo aggiungere tutti i metodi personalizzati che vogliamo. Tutti dovrebbero andare nel file [model].js
. Nel nostro caso, aggiungiamo un metodo remoto al modello Regalo per verificare se il regalo è già prenotato e uno per elencare tutti i regali che non sono riservati.
Per prima cosa, aggiungiamo una proprietà aggiuntiva al modello chiamata reserved
. Basta aggiungere questo alle proprietà in gift.json
:
... "reserved": { "type": "boolean" } ...
Il metodo remoto in gift.js
dovrebbe assomigliare a questo:
module.exports = function(Gift) { // method which lists all free gifts Gift.listFree = function(cb) { Gift.find({ fields: { reserved: false } }, cb); }; // expose the above method through the REST Gift.remoteMethod('listFree', { returns: { arg: 'gifts', type: 'array' }, http: { path: '/list-free', verb: 'get' } }); // method to return if the gift is free Gift.isFree = function(id, cb) { var response; Gift.find({ fields: { id: id } }, function(err, gift) { if (err) return cb(err); if (gift.reserved) response = 'Sorry, the gift is reserved'; else response = 'Great, this gift can be yours'; }); cb(null, response); }; // expose the method through REST Gift.remoteMethod('isFree', { accepts: { arg: 'id', type: 'number' }, returns: { arg: 'response', type: 'string' }, http: { path: '/free', verb: 'post' } }); };
Quindi, per scoprire se è disponibile un particolare regalo, il cliente può ora inviare una richiesta POST a /api/Gifts/free
, inserendo l' id
del regalo in questione.
Ganci remoti
A volte è necessario eseguire un metodo prima o dopo il metodo remoto. È possibile definire due tipi di hook remoti:
-
beforeRemote()
viene eseguito prima del metodo remoto. -
afterRemote()
viene eseguito dopo il metodo remoto.
In entrambi i casi, fornisci due argomenti: una stringa che corrisponde al metodo remoto a cui vuoi "agganciare" la tua funzione e la funzione di callback. Gran parte del potere degli hook remoti è che la stringa può includere caratteri jolly, quindi viene attivata da qualsiasi metodo di corrispondenza.
Nel nostro caso, impostiamo un hook per stampare le informazioni sulla console ogni volta che viene creato un nuovo donatore. Per fare ciò, aggiungiamo un hook "before create" in donor.js
:
module.exports = function(Donor) { Donor.beforeRemote('create', function(context, donor, next) { console.log('Saving new donor with name: ', context.req.body.name); next(); }); };
La richiesta viene chiamata con il context
specificato e il callback next()
nel middleware (discusso di seguito) viene chiamato dopo l'esecuzione dell'hook.
Controlli di accesso
Le applicazioni LoopBack accedono ai dati tramite modelli, quindi controllare l'accesso ai dati significa definire restrizioni sui modelli; ovvero, specificando chi o cosa può leggere e scrivere i dati o eseguire metodi sui modelli. I controlli di accesso LoopBack sono determinati da elenchi di controllo di accesso o ACL.
Consentiamo ai donatori e ai destinatari che non hanno effettuato l'accesso di visualizzare i regali, ma solo ai donatori che hanno effettuato l'accesso di crearli ed eliminarli.
$ slc loopback:acl
Per iniziare, neghiamo a tutti l'accesso a tutti gli endpoint.
? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: All (match all types) ? Select the role: All users ? Select the permission to apply: Explicitly deny access
Quindi, consenti a tutti di leggere dai modelli regalo:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Read ? Select the role: All users ? Select the permission to apply: Explicitly grant access
Quindi, vogliamo consentire agli utenti autenticati di creare regali:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: A single method ? Enter the method name: create ? Select the role: Any authenticated user ? Select the permission to apply: Explicitly grant access
E infine, consentiamo al proprietario del regalo di apportare eventuali modifiche:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Write ? Select the role: The user owning the object ? Select the permission to apply: Explicitly grant access
Ora, quando gift.json
, tutto dovrebbe essere a posto:
"acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" } ],
Una nota importante qui: $authenticated
è un ruolo predefinito che corrisponde a tutti gli utenti nel sistema (sia Donatori che Destinatari), ma vogliamo solo consentire ai Donatori di creare nuovi Doni. Pertanto, abbiamo bisogno di un ruolo personalizzato. Poiché il ruolo è un'altra entità che usciamo dagli schemi, possiamo sfruttare la sua chiamata API per creare il ruolo $authenticatedDonor
nella funzione di avvio, quindi modificare semplicemente gift.json
pricipalId
Sarà necessario creare un nuovo file, server/boot/script.js
e aggiungere il seguente codice:
Role.create({ name: 'authenticatedDonor' }, function(err, role) { if (err) return debug(err); })
L'entità RoleMapping associa i ruoli agli utenti. Assicurati che Role e RoleMapping siano entrambi esposti tramite REST. In server/model-config.json
, controlla che "public"
sia impostato su true
per l'entità Role. Quindi in donor.js
, possiamo scrivere un hook "before create" che mapperà l' userID
e il roleID
nella chiamata dell'API POST RoleMapping.
Middleware
Il middleware contiene funzioni che vengono eseguite quando viene effettuata una richiesta all'endpoint REST. Poiché LoopBack è basato su Express, utilizza il middleware Express con un concetto aggiuntivo, chiamato "fasi del middleware". Le fasi vengono utilizzate per definire chiaramente l'ordine in cui vengono chiamate le funzioni nel middleware.
Ecco l'elenco delle fasi predefinite, come fornito nei documenti di LoopBack:
- initial - Il primo punto in cui può essere eseguito il middleware.
- sessione - Prepara l'oggetto sessione.
- auth - Gestisce l'autenticazione e l'autorizzazione.
- parse - Analizza il corpo della richiesta.
- route : route HTTP che implementano la logica dell'applicazione. Il middleware registrato tramite l'API Express app.use, app.route, app.get (e altri verbi HTTP) viene eseguito all'inizio di questa fase. Utilizzare questa fase anche per app secondarie come loopback/server/middleware/rest o loopback-explorer.
- files - Serve risorse statiche (le richieste stanno raggiungendo il file system qui).
- final - Gestisci gli errori e le richieste di URL sconosciuti.
Ogni fase ha tre sottofasi. Ad esempio, le sottofasi della fase iniziale sono:
- iniziale: prima
- iniziale
- iniziale: dopo
Diamo una rapida occhiata al nostro middleware.json predefinito:
{ "initial:before": { "loopback#favicon": {} }, "initial": { "compression": {}, "cors": { "params": { "origin": true, "credentials": true, "maxAge": 86400 } } }, "session": { }, "auth": { }, "parse": { }, "routes": { }, "files": { }, "final": { "loopback#urlNotFound": {} }, "final:after": { "errorhandler": {} } }
Nella fase iniziale, chiamiamo loopback.favicon()
( loopback#favicon
è l'id del middleware per quella chiamata). Quindi, vengono chiamati i moduli npm di terze parti compression
e cors
(con o senza parametri). Nella fase finale, abbiamo altre due chiamate. urlNotFound
è una chiamata errorhandler
e il gestore degli errori è un modulo di terze parti. Questo esempio dovrebbe dimostrare che molte chiamate integrate possono essere utilizzate proprio come i moduli npm esterni. E, naturalmente, possiamo sempre creare il nostro middleware e chiamarli tramite questo file JSON.
loopback-boot
Per concludere, menzioniamo un modulo che esporta la funzione boot()
che inizializza l'applicazione. In server/server.js
troverai il seguente pezzo di codice, che esegue il bootstrap dell'applicazione:
boot(app, __dirname, function(err) { if (err) throw err; // start the server if `$ node server.js` if (require.main === module) app.start(); });
Questo script cercherà nel server/boot
e caricherà tutti gli script che trova lì in ordine alfabetico. Pertanto, in server/boot
, possiamo specificare qualsiasi script che dovrebbe essere eseguito all'avvio. Un esempio è explorer.js
, che esegue API Explorer , il client che abbiamo utilizzato per testare la nostra API.
Conclusione
Prima di lasciarvi, vorrei citare StrongLoop Arc, un'interfaccia utente grafica che può essere utilizzata come alternativa agli strumenti da riga di comando di slc
. Include anche strumenti per la creazione, la profilazione e il monitoraggio delle applicazioni Node. Per coloro che non sono fan della riga di comando, vale sicuramente la pena provare. Tuttavia, StrongLoop Arc sta per essere ritirato e la sua funzionalità viene integrata in IBM API Connect Developer Toolkit.
In generale, LoopBack può farti risparmiare un sacco di lavoro manuale poiché stai ottenendo molte cose fuori dagli schemi. Ti consente di concentrarti su problemi specifici dell'applicazione e sulla logica aziendale. Se la tua applicazione si basa su operazioni CRUD e manipolando entità predefinite, se sei stufo di riscrivere l'infrastruttura di autenticazione e autorizzazione dell'utente quando tonnellate di sviluppatori l'hanno scritta prima di te, o se vuoi sfruttare tutti i vantaggi di un ottimo framework web come Express, quindi la creazione della tua API REST con LoopBack può realizzare i tuoi sogni. È un pezzo di torta!