Let LoopBack Do It: una procedura dettagliata del framework dell'API Node che hai sempre sognato

Pubblicato: 2022-03-11

Inutile 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:

Struttura del progetto

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:
  • File JSON di definizione del modello, per convenzione denominati model-name .json ; ad esempio customer.json .
  • Script di modelli personalizzati per convenzione denominati model-name .js ; ad esempio, customer.js .
Per ulteriori informazioni, consulta File JSON di definizione del modello e Personalizzazione dei modelli.
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 .

Testare la tua API 2

Testare la tua API 3

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.

Testare la tua API 1

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.

Autenticazione utente

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:

  1. initial - Il primo punto in cui può essere eseguito il middleware.
  2. sessione - Prepara l'oggetto sessione.
  3. auth - Gestisce l'autenticazione e l'autorizzazione.
  4. parse - Analizza il corpo della richiesta.
  5. 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.
  6. files - Serve risorse statiche (le richieste stanno raggiungendo il file system qui).
  7. final - Gestisci gli errori e le richieste di URL sconosciuti.

Ogni fase ha tre sottofasi. Ad esempio, le sottofasi della fase iniziale sono:

  1. iniziale: prima
  2. iniziale
  3. 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.

Hai il blues della ripetizione? Non creare più quell'API Node da zero. Lascia che lo faccia LoopBack!
Twitta

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.

Conclusione

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!