Niech LoopBack to zrobi: przewodnik po frameworku Node API, o którym marzyłeś

Opublikowany: 2022-03-11

Nie trzeba wspominać o rosnącej popularności Node.js do tworzenia aplikacji. eBay prowadzi produkcyjną usługę Node API od 2011 roku. PayPal aktywnie przebudowuje swój front-end w Node. Mobilna strona Walmart stała się największą aplikacją Node pod względem ruchu. W Święto Dziękczynienia w 2014 r. serwery Walmart przetworzyły 1,5 miliarda żądań, z których 70 procent zostało dostarczonych za pośrednictwem urządzeń mobilnych i obsługiwanych przez Node.js. Po stronie rozwoju, Node Package Manager (npm) nadal szybko się rozwija, ostatnio przekraczając 150 000 hostowanych modułów.

Podczas gdy Ruby ma Railsy, ​​a Python ma Django, dominujący framework do tworzenia aplikacji dla Node nie został jeszcze ustanowiony. Ale jest potężny rywal, który zyskuje na popularności: LoopBack, platforma API typu open source zbudowana przez firmę StrongLoop z San Mateo w Kalifornii. StrongLoop jest ważnym współtwórcą najnowszej wersji Node, nie wspominając o długoletnich opiekunach Express, jednego z najpopularniejszych istniejących frameworków Node.

Przyjrzyjmy się bliżej LoopBack i jego możliwościom, przekładając wszystko na praktykę i budując przykładową aplikację.

Co to jest LoopBack i jak działa z Node?

LoopBack to framework do tworzenia interfejsów API i łączenia ich z backendowymi źródłami danych. Zbudowany na bazie Express, może przyjąć definicję modelu danych i łatwo wygenerować w pełni funkcjonalny, kompleksowy interfejs API REST, który może być wywoływany przez dowolnego klienta.

LoopBack ma wbudowanego klienta API Explorer . Użyjemy tego, ponieważ ułatwi to zobaczenie wyników naszej pracy, a nasz przykład może skupić się na budowaniu samego API.

Oczywiście będziesz potrzebować Node zainstalowanego na swoim komputerze, aby kontynuować. Zdobądź to tutaj. npm jest z nim dostarczany, dzięki czemu możesz łatwo zainstalować potrzebne pakiety. Zacznijmy.

Stwórz szkielet

Nasza aplikacja będzie zarządzać osobami, które chciałyby przekazać prezenty lub rzeczy, których już nie potrzebują, komuś, kto może ich potrzebować. Tak więc użytkownikami będą Dawcy i Odbiorcy. Darczyńca może stworzyć nowy prezent i zobaczyć listę prezentów. Odbiorca może zobaczyć listę prezentów od wszystkich użytkowników i może odebrać te, które nie zostały odebrane. Oczywiście, moglibyśmy budować dawców i odbiorców jako osobne role na tej samej encji (użytkowniku), ale spróbujmy je rozdzielić, abyśmy mogli zobaczyć, jak budować relacje w LoopBack. Nazwa tej przełomowej aplikacji będzie brzmiała Givesomebody .

Zainstaluj narzędzia wiersza poleceń StrongLoop przez npm:

 $ npm install -g strongloop

Następnie uruchom generator aplikacji LoopBack:

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

Dodajmy model. Nasz pierwszy model będzie nosił nazwę Gift. LoopBack poprosi o źródło danych i klasę bazową. Ponieważ nie ustawiliśmy jeszcze źródła danych, możemy umieścić db (memory) . Klasa bazowa jest automatycznie generowaną klasą modelu iw tym przypadku chcemy użyć PersistedModel , ponieważ zawiera już wszystkie zwykłe dla nas metody CRUD. Następnie LoopBack pyta, czy powinien ujawnić model przez REST (tak) i nazwę usługi REST. Naciśnij tutaj enter, aby użyć wartości domyślnej, która jest po prostu liczbą mnogą nazwy modelu (w naszym przypadku 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):

Na koniec podajemy nazwy właściwości, ich typy danych oraz flagi wymagane/niewymagane. Prezent będzie miał właściwości 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

Wprowadź pustą nazwę właściwości, aby wskazać, że zakończyłeś definiowanie właściwości.

Generator modeli utworzy dwa pliki, które definiują model w common/models aplikacji: gift.json i gift.js . Plik JSON określa wszystkie metadane dotyczące jednostki: właściwości, relacje, walidacje, role i nazwy metod. Plik JavaScript jest używany do definiowania dodatkowych zachowań i określania zdalnych podpięć, które mają być wywoływane przed lub po pewnych operacjach (np. tworzenie, aktualizacja lub usuwanie).

Pozostałe dwie jednostki modelowe będą naszymi modelami Dawcy i Odbiorcy. Możemy je stworzyć przy użyciu tego samego procesu, z tą różnicą, że tym razem postawmy User jako klasę bazową. Da nam to pewne właściwości, takie jak username , password , email po wyjęciu z pudełka. Możemy dodać tylko nazwę i kraj, na przykład, aby mieć pełną encję. Dla Odbiorcy chcemy również dodać adres dostawy.

Struktura projektu

Rzućmy okiem na wygenerowaną strukturę projektu:

Struktura projektu

Trzy główne katalogi to: - /server – Zawiera skrypty aplikacji węzła i pliki konfiguracyjne. - /client – ​​zawiera .js, .html, .css i wszystkie inne pliki statyczne. - /common — ten folder jest wspólny zarówno dla serwera, jak i klienta. Pliki modeli znajdują się tutaj.

Oto szczegółowy podział zawartości każdego katalogu, zaczerpnięty z dokumentacji LoopBack:

Plik lub katalog Opis Jak uzyskać dostęp w kodzie?
Katalog aplikacji najwyższego poziomu
package.json Specyfikacja standardowego pakietu npm. Zobacz pakiet.json Nie dotyczy
/katalog serwera - Pliki aplikacji węzła
server.js Główny plik programu aplikacyjnego. Nie dotyczy
config.json Ustawienia aplikacji. Zobacz plik config.json. app.get('setting-name')
datasources.json Plik konfiguracyjny źródła danych. Zobacz datasources.json. Na przykład zobacz Tworzenie nowego źródła danych . app.datasources['datasource-name']
model-config.json Plik konfiguracji modelu. Zobacz model-config.json. Aby uzyskać więcej informacji, zobacz Łączenie modeli ze źródłami danych . Nie dotyczy
middleware.json Plik definicji oprogramowania pośredniego. Aby uzyskać więcej informacji, zobacz Definiowanie oprogramowania pośredniczącego. Nie dotyczy
/boot Dodaj skrypty, aby wykonać inicjalizację i konfigurację. Zobacz skrypty rozruchowe. Skrypty są wykonywane automatycznie w kolejności alfabetycznej.
/katalog klienta - pliki aplikacji klienta
README.md Generatory LoopBack tworzą pusty plik README w formacie przeceny. Nie dotyczy
Inny Dodaj swoje pliki HTML, CSS, klienta JavaScript.
/wspólny katalog - współdzielone pliki aplikacji
/models Pliki modeli niestandardowych:
  • Pliki JSON definicji modelu, zgodnie z konwencją o nazwie model-name .json ; na przykład customer.json .
  • Skrypty modelu niestandardowego według konwencji o nazwie model-name .js ; na przykład customer.js .
Aby uzyskać więcej informacji, zobacz plik JSON definicji modelu i Dostosowywanie modeli.
Węzeł:
myModel = app.models.myModelName

Budować relacje

W naszym przykładzie mamy do modelowania kilka ważnych relacji. Darczyńca może przekazać wiele Darów, co daje relację Darczyńca ma wiele Darów . Odbiorca może również otrzymać wiele Prezentów, więc mamy również relację Odbiorca ma wiele Prezentów . Z drugiej strony Prezent należy do Dawcy i może również należeć do Odbiorcy , jeśli Odbiorca zdecyduje się go przyjąć. Przełóżmy to na język 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

Zauważ, że nie ma modelu przelotowego; po prostu trzymamy odniesienie do Daru.

Jeśli powtórzymy powyższą procedurę dla Odbiorcy i dodamy dwa należące do relacji do Prezentu, wykonamy nasz projekt modelu na tylnej stronie. LoopBack automatycznie aktualizuje pliki JSON dla modeli, aby dokładnie wyrazić to, co właśnie zrobiliśmy za pomocą tych prostych okien dialogowych:

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

Dodaj źródło danych

Zobaczmy teraz, jak dołączyć prawdziwe źródło danych do przechowywania wszystkich danych naszej aplikacji. Na potrzeby tego przykładu użyjemy MongoDB, ale LoopBack ma moduły do ​​łączenia się z Oracle, MySQL, PostgreSQL, Redis i SQL Server.

Najpierw zainstaluj złącze:

 $ npm install --save loopback-connector-mongodb

Następnie dodaj źródło danych do swojego projektu:

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

Następnym krokiem jest skonfigurowanie źródła danych w server/datasources.json . Użyj tej konfiguracji dla lokalnego serwera MongoDB:

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

Na koniec otwórz server/model-config.json i zmień datasource dla wszystkich encji, które chcemy zachować w bazie danych, na "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 } }

Testowanie interfejsu API REST

Czas zobaczyć, co zbudowaliśmy do tej pory! Użyjemy niesamowitego wbudowanego narzędzia API Explorer , które może być używane jako klient dla właśnie utworzonej usługi. Spróbujmy przetestować wywołania REST API.

W osobnym oknie uruchom MongoDB z:

 $ mongod

Uruchom aplikację za pomocą:

 $ node .

W przeglądarce przejdź do http://localhost:3000/explorer/ . Możesz zobaczyć swoje podmioty z listą dostępnych operacji. Spróbuj dodać jednego Donor za pomocą wywołania POST /Donors .

Testowanie Twojego API 2

Testowanie Twojego API 3

API Explorer jest bardzo intuicyjny; wybierz dowolną z ujawnionych metod, a odpowiedni schemat modelu zostanie wyświetlony w prawym dolnym rogu. W obszarze tekstowym data można napisać niestandardowe żądanie HTTP. Po wypełnieniu żądania kliknij przycisk „Wypróbuj”, a odpowiedź serwera zostanie wyświetlona poniżej.

Testowanie Twojego API 1

Uwierzytelnianie użytkownika

Jak wspomniano powyżej, jedną z encji, która jest wstępnie zbudowana z LoopBack, jest klasa User. Użytkownik posiada metody logowania i wylogowania i może być powiązany z encją AccessToken, która przechowuje token konkretnego użytkownika. W rzeczywistości kompletny system uwierzytelniania użytkowników jest gotowy do użycia po wyjęciu z pudełka. Jeśli spróbujemy wywołać /Donors/login przez API Explorer , oto odpowiedź, którą otrzymamy:

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

id jest w rzeczywistości wartością AccessToken, wygenerowaną i utrwaloną w bazie danych automatycznie. Jak widać tutaj, możliwe jest ustawienie tokena dostępu i używanie go przy każdym kolejnym żądaniu.

Uwierzytelnianie użytkownika

Metody zdalne

Metoda zdalna to statyczna metoda modelu, uwidoczniona w niestandardowym punkcie końcowym REST. Metody zdalne mogą być używane do wykonywania operacji, których nie zapewnia standardowy model API REST LoopBack.

Oprócz metod CRUD, które otrzymujemy po wyjęciu z pudełka, możemy dodać tyle niestandardowych metod, ile chcemy. Wszystkie powinny znaleźć się w pliku [model].js . W naszym przypadku dodajmy metodę zdalną do modelu Prezent, aby sprawdzić, czy prezent jest już zarezerwowany, oraz metodę, aby wyświetlić listę wszystkich prezentów, które nie są zarezerwowane.

Najpierw dodajmy do modelu dodatkową właściwość o nazwie reserved . Po prostu dodaj to do właściwości w gift.json :

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

Metoda zdalna w gift.js powinna wyglądać mniej więcej tak:

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

Aby dowiedzieć się, czy dany prezent jest dostępny, klient może teraz wysłać żądanie POST do /api/Gifts/free , podając id danego prezentu.

Zdalne haki

Czasami istnieje potrzeba wykonania jakiejś metody przed lub po metodzie zdalnej. Możesz zdefiniować dwa rodzaje zdalnych podpięć:

  • beforeRemote() działa przed metodą zdalną.
  • afterRemote() działa po zdalnej metodzie.

W obu przypadkach podajesz dwa argumenty: ciąg znaków, który pasuje do zdalnej metody, do której chcesz „podpiąć” swoją funkcję, oraz funkcję wywołania zwrotnego. Dużą zaletą zdalnych podpięć jest to, że łańcuch może zawierać symbole wieloznaczne, więc jest wyzwalany przez dowolną metodę dopasowania.

W naszym przypadku ustawmy podpięcie, aby wyświetlać informacje w konsoli za każdym razem, gdy tworzony jest nowy dawca. Aby to osiągnąć, dodajmy hak „przed utworzeniem” w 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(); }); };

Żądanie jest wywoływane z podanym context , a wywołanie zwrotne next() w oprogramowaniu pośredniczącym (omówione poniżej) jest wywoływane po uruchomieniu przechwytu.

Kontrola dostępu

Aplikacje LoopBack uzyskują dostęp do danych za pośrednictwem modeli, więc kontrolowanie dostępu do danych oznacza definiowanie ograniczeń dotyczących modeli; czyli określenie, kto lub co może odczytywać i zapisywać dane lub wykonywać metody na modelach. Kontrole dostępu LoopBack są określane przez listy kontroli dostępu lub listy ACL.

Pozwólmy niezalogowanym Darczyńcom i Odbiorcom przeglądać prezenty, ale tylko zalogowani Darczyńcy mogą je tworzyć i usuwać.

 $ slc loopback:acl

Na początek odmówmy wszystkim dostępu do wszystkich punktów końcowych.

 ? 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

Następnie pozwól wszystkim czytać z modeli prezentów:

 $ 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

Następnie chcemy umożliwić uwierzytelnionym użytkownikom tworzenie Prezentów:

 $ 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 na koniec pozwólmy właścicielowi prezentu na wprowadzenie jakichkolwiek zmian:

 $ 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

Teraz, gdy przeglądamy gift.json , wszystko powinno być na swoim miejscu:

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

Jedna ważna uwaga: $authenticated to predefiniowana rola, która odpowiada wszystkim użytkownikom w systemie (zarówno Darczyńcom, jak i Odbiorcom), ale chcemy tylko pozwolić Darczyńcom na tworzenie nowych Prezentów. Dlatego potrzebujemy niestandardowej roli. Ponieważ rola jest kolejną jednostką, którą otrzymujemy po wyjęciu z pudełka, możemy wykorzystać jej wywołanie API do utworzenia roli $authenticatedDonor w funkcji rozruchu, a następnie po prostu zmodyfikować pricipalId w gift.json .

Konieczne będzie utworzenie nowego pliku server/boot/script.js i dodanie następującego kodu:

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

Jednostka RoleMapping mapuje role na użytkowników. Upewnij się, że role i mapowanie ról są ujawniane przez REST. W server/model-config.json sprawdź, czy "public" jest ustawione na true dla encji Role. Następnie w donor.js możemy napisać hook „przed utworzeniem”, który roleID identyfikator userID i identyfikator roli w wywołaniu funkcji RoleMapping POST API.

Oprogramowanie pośredniczące

Oprogramowanie pośredniczące zawiera funkcje, które są wykonywane po przesłaniu żądania do punktu końcowego REST. Ponieważ LoopBack jest oparty na Express, wykorzystuje oprogramowanie pośredniczące Express z jedną dodatkową koncepcją, zwaną „fazami oprogramowania pośredniczącego”. Fazy ​​służą do jasnego określenia kolejności wywoływania funkcji w oprogramowaniu pośredniczącym.

Oto lista wstępnie zdefiniowanych faz, zgodnie z dokumentacją LoopBack:

  1. Initial — pierwszy punkt, w którym może działać oprogramowanie pośredniczące.
  2. session — Przygotuj obiekt sesji.
  3. auth — obsługa uwierzytelniania i autoryzacji.
  4. parse — przeanalizuj treść żądania.
  5. trasy — trasy HTTP implementujące logikę aplikacji. Oprogramowanie pośredniczące zarejestrowane za pośrednictwem interfejsu Express API app.use, app.route, app.get (i innych czasowników HTTP) jest uruchamiane na początku tej fazy. Użyj tej fazy również dla pod-aplikacji, takich jak loopback/serwer/middleware/rest lub loopback-explorer.
  6. pliki — udostępniaj zasoby statyczne (żądania trafiają tutaj do systemu plików).
  7. final – Rozwiąż błędy i żądania dotyczące nieznanych adresów URL.

Każda faza ma trzy podfazy. Na przykład podfazy fazy początkowej to:

  1. początkowe:przed
  2. Inicjał
  3. początkowe:po

Rzućmy okiem na nasz domyślny plik middleware.json:

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

W początkowej fazie wywołujemy loopback.favicon() ( loopback#favicon jest identyfikatorem oprogramowania pośredniego dla tego wywołania). Następnie wywoływane są compression i cors modułów npm innych firm (z parametrami lub bez). W końcowej fazie mamy jeszcze dwa wezwania. urlNotFound to wywołanie LoopBack, a errorhandler to moduł innej firmy. Ten przykład powinien pokazać, że wiele wbudowanych wywołań może być używanych podobnie jak zewnętrzne moduły npm. I oczywiście zawsze możemy stworzyć własne oprogramowanie pośredniczące i wywołać je za pośrednictwem tego pliku JSON.

loopback-boot

Na zakończenie wspomnijmy o module, który eksportuje funkcję boot() , która inicjuje aplikację. W server/server.js znajdziesz następujący fragment kodu, który uruchamia aplikację:

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

Ten skrypt przeszuka folder server/boot i załaduje wszystkie znalezione tam skrypty w kolejności alfabetycznej. W ten sposób w server/boot możemy określić dowolny skrypt, który powinien zostać uruchomiony przy starcie. Jednym z przykładów jest explorer.js , który uruchamia API Explorer , klienta, którego używaliśmy do testowania naszego API.

Masz bluesa powtórzeń? Nie twórz ponownie tego interfejsu Node API od podstaw. Niech LoopBack to zrobi!
Ćwierkać

Wniosek

Zanim was opuszczę, chciałbym wspomnieć o StrongLoop Arc, graficznym interfejsie użytkownika, który może być używany jako alternatywa dla narzędzi wiersza poleceń slc . Zawiera również narzędzia do budowania, profilowania i monitorowania aplikacji Node. Dla tych, którzy nie są fanami wiersza poleceń, zdecydowanie warto spróbować. Jednak StrongLoop Arc wkrótce zostanie wycofany, a jego funkcjonalność zostanie zintegrowana z pakietem IBM API Connect Developer Toolkit.

Wniosek

Ogólnie rzecz biorąc, LoopBack może zaoszczędzić wiele ręcznej pracy, ponieważ otrzymujesz wiele rzeczy po wyjęciu z pudełka. Pozwala skupić się na problemach specyficznych dla aplikacji i logice biznesowej. Jeśli Twoja aplikacja jest oparta na operacjach CRUD i manipuluje predefiniowanymi jednostkami, jeśli masz dość przepisywania infrastruktury uwierzytelniania i autoryzacji użytkownika, gdy wielu programistów napisało to przed Tobą, lub jeśli chcesz wykorzystać wszystkie zalety świetnego frameworka internetowego, takiego jak Express, a następnie zbudowanie interfejsu API REST za pomocą LoopBack może spełnić Twoje marzenia. To bułka z masłem!