Let LoopBack Do It: O prezentare a cadrului API Node la care ați visat

Publicat: 2022-03-11

Este inutil să menționăm popularitatea în creștere a Node.js pentru dezvoltarea de aplicații. eBay rulează un serviciu API Node de producție din 2011. PayPal își reconstruiește în mod activ front-end-ul în Node. Site-ul mobil al Walmart a devenit cea mai mare aplicație Node, din punct de vedere al traficului. În weekendul de Ziua Recunoștinței din 2014, serverele Walmart au procesat 1,5 miliarde de solicitări, dintre care 70% au fost livrate prin dispozitive mobile și alimentate de Node.js. Pe partea de dezvoltare, managerul de pachete Node (npm) continuă să crească rapid, depășind recent 150.000 de module găzduite.

În timp ce Ruby are Rails și Python are Django, cadrul dominant de dezvoltare a aplicațiilor pentru Node nu a fost încă stabilit. Dar, există un concurent puternic care câștigă abur: LoopBack, un cadru API open source construit de compania StrongLoop din San Mateo, California. StrongLoop este un contribuitor important la cea mai recentă versiune Node, ca să nu mai vorbim de menținătorii de lungă durată ai Express, unul dintre cele mai populare framework-uri Node existente.

Să aruncăm o privire mai atentă la LoopBack și la capacitățile sale, transformând totul în practică și construind un exemplu de aplicație.

Ce este LoopBack și cum funcționează cu Node?

LoopBack este un cadru pentru crearea API-urilor și conectarea acestora cu surse de date backend. Construit pe Express, poate lua o definiție a modelului de date și poate genera cu ușurință un API REST complet funcțional, care poate fi apelat de orice client.

LoopBack vine cu un client încorporat, API Explorer . Vom folosi acest lucru, deoarece face mai ușor să vedeți rezultatele muncii noastre și astfel încât exemplul nostru să se poată concentra pe construirea API-ului în sine.

Desigur, veți avea nevoie de Node instalat pe mașina dvs. pentru a urma. Adu-o aici. npm vine cu el, astfel încât să puteți instala cu ușurință pachetele necesare. Să începem.

Creați un schelet

Aplicația noastră va gestiona persoanele care ar dori să doneze cadouri sau lucruri de care pur și simplu nu mai au nevoie, cuiva care ar putea avea nevoie de ele. Deci, utilizatorii vor fi donatori și receptori. Un donator poate crea un nou cadou și poate vedea lista de cadouri. Un Destinatar poate vedea lista de cadouri de la toți utilizatorii și poate revendica pe cele nerevendicate. Desigur, am putea construi Donori și Receptori ca roluri separate pe aceeași entitate (Utilizator), dar să încercăm să le separăm, astfel încât să putem vedea cum să construim relații în LoopBack. Numele acestei aplicații inovatoare va fi Givesomebody .

Instalați instrumentele de linie de comandă StrongLoop prin npm:

 $ npm install -g strongloop

Apoi rulați generatorul de aplicații LoopBack:

 $ slc loopback _-----_ | | .--------------------------. |--(o)--| | Let's create a LoopBack | `--------- | application! | ( _U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ` |° Y ` ? What's the name of your application? Givesomebody

Să adăugăm un model. Primul nostru model se va numi Gift. LoopBack va cere sursa de date și clasa de bază. Deoarece nu am configurat încă sursa de date, putem pune db (memory) . Clasa de bază este o clasă de model generată automat și dorim să folosim PersistedModel în acest caz, deoarece conține deja toate metodele CRUD obișnuite pentru noi. Apoi, LoopBack întreabă dacă ar trebui să expună modelul prin REST (da) și numele serviciului REST. Apăsați enter aici pentru a utiliza valoarea implicită, care este pur și simplu pluralul numelui modelului (în cazul nostru, 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):

În cele din urmă, dăm numele proprietăților, tipurile de date ale acestora și semnalizatoarele obligatorii/nesolicitate. Cadoul va avea proprietăți de name și 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

Introduceți un nume de proprietate gol pentru a indica că ați terminat de definit proprietățile.

Generatorul de modele va crea două fișiere care definesc modelul în common/models aplicației: gift.json și gift.js . Fișierul JSON specifică toate metadatele despre entitate: proprietăți, relații, validări, roluri și nume de metode. Fișierul JavaScript este folosit pentru a defini comportamentul suplimentar și pentru a specifica cârlige la distanță care urmează să fie apelate înainte sau după anumite operațiuni (de exemplu, crearea, actualizarea sau ștergerea).

Celelalte două entități model vor fi modelele noastre Donator și Receiver. Le putem crea folosind același proces, cu excepția de data aceasta să punem User ca clasă de bază. Ne va oferi câteva proprietăți, cum ar fi numele de username , password , e- email din cutie. Putem adăuga doar numele și țara, de exemplu, pentru a avea o entitate completă. Pentru destinatar dorim să adăugăm și adresa de livrare.

Structura proiectului

Să aruncăm o privire asupra structurii proiectului generat:

Structura proiectului

Cele trei directoare principale sunt: ​​- /server – Conține scripturi de aplicație nod și fișiere de configurare. - /client – ​​Conține .js, .html, .css și toate celelalte fișiere statice. - /common – Acest folder este comun atât pentru server, cât și pentru client. Fișierele modelului merg aici.

Iată o defalcare detaliată a conținutului fiecărui director, luată din documentația LoopBack:

Fișier sau director Descriere Cum se accesează în cod
Directorul de aplicații de nivel superior
package.json Specificația pachetului standard npm. Vedeți package.json N / A
Director /server - Fișierele aplicației Node
server.js Fișierul principal al programului aplicației. N / A
config.json Setările aplicației. Vedeți config.json. app.get('setting-name')
datasources.json Fișierul de configurare a sursei de date. Vedeți datasources.json. Pentru un exemplu, consultați Crearea unei noi surse de date . app.datasources['datasource-name']
model-config.json Fișierul de configurare a modelului. Vedeți model-config.json. Pentru mai multe informații, vezi Conectarea modelelor la sursele de date . N / A
middleware.json Fișier de definiție middleware. Pentru mai multe informații, consultați Definirea middleware-ului. N / A
directorul /boot Adăugați scripturi pentru a efectua inițializarea și configurarea. Vedeți scripturile de pornire. Scripturile sunt executate automat în ordine alfabetică.
directorul /client - fișierele aplicației client
README.md Generatoarele LoopBack creează un fișier README gol în format de reducere. N / A
Alte Adăugați fișierele HTML, CSS, JavaScript client.
/director comun - fișiere de aplicație partajate
directorul /models Fișiere model personalizate:
  • Fișiere JSON de definiție a modelului, denumite prin convenție nume model-name .json ; de exemplu customer.json .
  • Scripturi de model personalizate prin convenție numite model-name .js ; de exemplu, customer.js .
Pentru mai multe informații, consultați Fișierul JSON cu definiția modelului și Personalizarea modelelor.
Nodul:
myModel = app.models.myModelName

Construiți relații

În exemplul nostru, avem câteva relații importante de modelat. Un Donator poate dona multe Cadouri, ceea ce dă relația Donatorul are multe Cadouri . Un Destinator poate primi, de asemenea, multe Cadouri, deci avem și relația Destinatarul are multe Cadouri . Pe de altă parte, Cadou aparține Donatorului și poate aparține și Destinatarului dacă acesta alege să-l accepte. Să punem asta în limbajul 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

Rețineți că nu există un model prin intermediul; ținem doar referința la Dar.

Dacă repetăm ​​procedura de mai sus pentru Receiver și adăugăm două aparțin relațiilor la Gift, vom realiza modelul nostru pe o parte din spate. LoopBack actualizează automat fișierele JSON pentru modele pentru a exprima exact ceea ce tocmai am făcut prin aceste dialoguri simple:

 // common/models/donor.json ... "relations": { "gifts": { "type": "hasMany", "model": "Gift", "foreignKey": "" } }, ...

Adăugați o sursă de date

Acum să vedem cum să atașăm o sursă de date reală pentru a stoca toate datele aplicației noastre. În scopul acestui exemplu, vom folosi MongoDB, dar LoopBack are module pentru a se conecta la Oracle, MySQL, PostgreSQL, Redis și SQL Server.

Mai întâi, instalați conectorul:

 $ npm install --save loopback-connector-mongodb

Apoi, adăugați o sursă de date la proiectul dvs.:

 $ slc loopback:datasource ? Enter the data-source name: givesomebody ? Select the connector for givesomebody: MongoDB (supported by StrongLoop)

Următorul pas este să vă configurați sursa de date în server/datasources.json . Utilizați această configurație pentru un server MongoDB local:

 ... "givesomebody": { "name": "givesomebody", "connector": "mongodb", "host": "localhost", "port": 27017, "database": "givesomebody", "username": "", "password": "" } ...

În cele din urmă, deschideți server/model-config.json datasource modificați sursa de date pentru toate entitățile pe care dorim să le menținem în baza de date în "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 } }

Testarea API-ului dvs. REST

Este timpul să vedem ce am construit până acum! Vom folosi minunatul instrument încorporat, API Explorer , care poate fi folosit ca client pentru serviciul pe care tocmai l-am creat. Să încercăm să testăm apelurile API REST.

Într-o fereastră separată, porniți MongoDB cu:

 $ mongod

Rulați aplicația cu:

 $ node .

În browser, accesați http://localhost:3000/explorer/ . Vă puteți vedea entitățile cu lista de operațiuni disponibile. Încercați să adăugați un donator cu un apel POST /Donors .

Testarea API-ului dvs. 2

Testarea API-ului dvs. 3

API Explorer este foarte intuitiv; selectați oricare dintre metodele expuse, iar schema de model corespunzătoare va fi afișată în colțul din dreapta jos. În zona de text de data , este posibil să scrieți o solicitare HTTP personalizată. Odată ce solicitarea este completată, faceți clic pe butonul „Încercați”, iar răspunsul serverului va fi afișat mai jos.

Testarea API-ului dvs. 1

Autentificarea utilizatorului

După cum am menționat mai sus, una dintre entitățile care vine pre-construit cu LoopBack este clasa User. Utilizatorul posedă metode de conectare și deconectare și poate fi legat de o entitate AccessToken care păstrează simbolul utilizatorului specific. De fapt, un sistem complet de autentificare a utilizatorului este gata să iasă din cutie. Dacă încercăm să apelăm /Donors/login prin API Explorer , iată răspunsul pe care îl primim:

 { "id": "9Kvp4zc0rTrH7IMMeRGwTNc6IqNxpVfv7D17DEcHHsgcAf9Z36A3CnPpZJ1iGrMS", "ttl": 1209600, "created": "2015-05-26T01:24:41.561Z", "userId": "" }

id -ul este de fapt valoarea AccessToken-ului, generat și persistat în baza de date automat. După cum vedeți aici, este posibil să setați un token de acces și să îl utilizați pentru fiecare solicitare ulterioară.

Autentificarea utilizatorului

Metode de la distanță

O metodă la distanță este o metodă statică a unui model, expusă peste un punct final REST personalizat. Metodele de la distanță pot fi utilizate pentru a efectua operațiuni care nu sunt furnizate de modelul standard REST API al LoopBack.

Pe lângă metodele CRUD pe care le scoatem din cutie, putem adăuga câte metode personalizate dorim. Toate ar trebui să intre în fișierul [model].js . În cazul nostru, să adăugăm o metodă la distanță modelului Cadou pentru a verifica dacă cadoul este deja rezervat și una pentru a enumera toate cadourile care nu sunt rezervate.

Mai întâi, să adăugăm o proprietate suplimentară modelului numită reserved . Doar adăugați acest lucru la proprietățile din gift.json :

 ... "reserved": { "type": "boolean" } ...

Metoda de la distanță din gift.js ar trebui să arate cam așa:

 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' } }); };

Așa că pentru a afla dacă un anumit cadou este disponibil, clientul poate trimite acum o solicitare POST către /api/Gifts/free , trecându-i id -ul cadoului în cauză.

Cârlige de la distanță

Uneori este nevoie de execuția unei metode înainte sau după metoda de la distanță. Puteți defini două tipuri de cârlige de la distanță:

  • beforeRemote() rulează înaintea metodei de la distanță.
  • afterRemote() rulează după metoda de la distanță.

În ambele cazuri, furnizați două argumente: un șir care se potrivește cu metoda de la distanță la care doriți să vă „conectați” funcția și funcția de apel invers. O mare parte din puterea cârligelor de la distanță este că șirul poate include metacaractere, deci este declanșat de orice metodă de potrivire.

În cazul nostru, să setăm un cârlig pentru a imprima informații pe consolă ori de câte ori este creat un nou Donor. Pentru a realiza acest lucru, să adăugăm un cârlig „înainte de a crea” în 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(); }); };

Solicitarea este apelată cu context dat, iar callback-ul next() din middleware (discutat mai jos) este apelat după rularea hook-ului.

Controale de acces

Aplicațiile LoopBack accesează datele prin modele, astfel încât controlul accesului la date înseamnă definirea restricțiilor asupra modelelor; adică specificarea cine sau ce poate citi și scrie datele sau executa metode pe modele. Controalele de acces LoopBack sunt determinate de listele de control al accesului sau ACL-uri.

Să permitem donatorilor și primitorilor neconectați să vadă cadouri, dar numai donatorilor conectați să le creeze și să le ștergă.

 $ slc loopback:acl

Pentru început, să refuzăm tuturor accesul la toate punctele finale.

 ? 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

Apoi, permiteți tuturor să citească din modelele Cadou:

 $ 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

Apoi, dorim să permitem utilizatorilor autentificați să creeze cadouri:

 $ 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

Și, în sfârșit, să permitem proprietarului cadoului să facă orice modificări:

 $ 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

Acum, când examinăm gift.json , totul ar trebui să fie la locul lui:

 "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" } ],

O notă importantă aici: $authenticated este un rol predefinit care corespunde tuturor utilizatorilor din sistem (atât Donatorii, cât și Beneficiarii), dar vrem doar să le permitem Donatorilor să creeze noi Cadouri. Prin urmare, avem nevoie de un rol personalizat. Deoarece Role este încă o entitate pe care o ieșim din cutie, putem folosi apelul său API pentru a crea rolul $authenticatedDonor în funcția de pornire și apoi doar să modificăm gift.json pricipalId

Va fi necesar să creați un fișier nou, server/boot/script.js și să adăugați următorul cod:

 Role.create({ name: 'authenticatedDonor' }, function(err, role) { if (err) return debug(err); })

Entitatea RoleMapping mapează rolurile la utilizatori. Asigurați-vă că Role și RoleMapping sunt ambele expuse prin REST. În server/model-config.json , verificați dacă "public" este setat la true pentru entitatea Role. Apoi, în donor.js , putem scrie un cârlig „înainte de a crea” care va mapa userID -ul utilizatorului și ID-ul rolului în apelul API roleID POST.

Middleware

Middleware-ul conține funcții care sunt executate atunci când se face o solicitare către punctul final REST. Deoarece LoopBack se bazează pe Express, folosește middleware Express cu un concept suplimentar, numit „faze de middleware”. Fazele sunt folosite pentru a defini clar ordinea în care sunt apelate funcțiile din middleware.

Iată lista fazelor predefinite, conform documentelor LoopBack:

  1. initial - primul punct la care se poate rula middleware.
  2. sesiune - Pregătește obiectul sesiune.
  3. auth - Gestionează autentificarea și autorizarea.
  4. parse - Analizează corpul cererii.
  5. rute - rute HTTP care implementează logica aplicației. Middleware înregistrat prin intermediul API-ului Express app.use, app.route, app.get (și alte verbe HTTP) rulează la începutul acestei faze. Utilizați această fază și pentru sub-aplicații precum loopback/server/middleware/rest sau loopback-explorer.
  6. fișiere - Serviți active statice (cererile ajung în sistemul de fișiere aici).
  7. final - Tratează erorile și solicitările de adrese URL necunoscute.

Fiecare fază are trei subfaze. De exemplu, subfazele fazei inițiale sunt:

  1. initial: inainte
  2. iniţială
  3. initial:dupa

Să aruncăm o privire rapidă asupra middleware.json nostru implicit:

 { "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": {} } }

În faza inițială, apelăm loopback.favicon() ( loopback#favicon este id-ul middleware-ului pentru apelul respectiv). Apoi, modulele terțe npm compression și cors sunt apelate (cu sau fără parametri). În faza finală, mai avem două apeluri. urlNotFound este un apel LoopBack, iar errorhandler este un modul terță parte. Acest exemplu ar trebui să demonstreze că o mulțime de apeluri încorporate pot fi utilizate la fel ca modulele externe npm. Și, desigur, putem crea oricând propriul nostru middleware și le putem apela prin acest fișier JSON.

loopback-boot

Pentru a încheia, să menționăm un modul care exportă funcția boot() care inițializează aplicația. În server/server.js veți găsi următoarea bucată de cod, care pornește aplicația:

 boot(app, __dirname, function(err) { if (err) throw err; // start the server if `$ node server.js` if (require.main === module) app.start(); });

Acest script va căuta în folderul server/boot și va încărca toate scripturile pe care le găsește acolo în ordine alfabetică. Astfel, în server/boot , putem specifica orice script care ar trebui să fie rulat la pornire. Un exemplu este explorer.js , care rulează API Explorer , clientul pe care l-am folosit pentru a testa API-ul nostru.

Ai blues-ul de repetiție? Nu construiți din nou acel API Node de la zero. Lasă-l pe LoopBack!
Tweet

Concluzie

Înainte de a vă părăsi, aș dori să menționez StrongLoop Arc, o interfață grafică care poate fi folosită ca alternativă la instrumentele de linie de comandă slc . Include, de asemenea, instrumente pentru construirea, profilarea și monitorizarea aplicațiilor Node. Pentru cei care nu sunt fani ai liniei de comandă, acest lucru merită cu siguranță încercat. Cu toate acestea, StrongLoop Arc este pe cale să fie depreciat, iar funcționalitatea sa este integrată în IBM API Connect Developer Toolkit.

Concluzie

În general, LoopBack vă poate economisi multă muncă manuală, deoarece obțineți o mulțime de lucruri din cutie. Vă permite să vă concentrați pe probleme specifice aplicației și pe logica de afaceri. Dacă aplicația dvs. se bazează pe operațiuni CRUD și pe manipularea entităților predefinite, dacă v-ați săturat să rescrieți infrastructura de autentificare și autorizare a utilizatorului când tone de dezvoltatori au scris asta înaintea dvs. sau dacă doriți să profitați de toate avantajele unui cadru web grozav, cum ar fi Express, apoi construirea API-ului REST cu LoopBack vă poate transforma visele în realitate. E o nimica toata!