Laissez LoopBack le faire : présentation du framework d'API Node dont vous rêviez

Publié: 2022-03-11

Il est inutile de mentionner la popularité croissante de Node.js pour le développement d'applications. eBay gère un service d'API Node de production depuis 2011. PayPal reconstruit activement son front-end dans Node. Le site mobile de Walmart est devenu la plus grande application Node, en termes de trafic. Le week-end de Thanksgiving en 2014, les serveurs Walmart ont traité 1,5 milliard de requêtes, dont 70 % ont été transmises via mobile et alimentées par Node.js. Côté développement, le gestionnaire de packages Node (npm) continue de croître rapidement, dépassant récemment les 150 000 modules hébergés.

Alors que Ruby a Rails et Python a Django, le framework de développement d'applications dominant pour Node n'a pas encore été établi. Mais il y a un concurrent puissant qui gagne du terrain : LoopBack, un framework API open source construit par San Mateo, Californie, société StrongLoop. StrongLoop est un contributeur important à la dernière version de Node, sans parler des mainteneurs de longue date d'Express, l'un des frameworks Node les plus populaires qui existent.

Examinons de plus près LoopBack et ses capacités en transformant tout en pratique et en créant un exemple d'application.

Qu'est-ce que LoopBack et comment fonctionne-t-il avec Node ?

LoopBack est un framework permettant de créer des API et de les connecter à des sources de données backend. Construit sur Express, il peut prendre une définition de modèle de données et générer facilement une API REST de bout en bout entièrement fonctionnelle qui peut être appelée par n'importe quel client.

LoopBack est livré avec un client intégré, API Explorer . Nous allons l'utiliser car cela permet de voir plus facilement les résultats de notre travail, et pour que notre exemple puisse se concentrer sur la construction de l'API elle-même.

Vous aurez bien sûr besoin de Node installé sur votre machine pour suivre. Obtenez-le ici. npm est livré avec, vous pouvez donc installer facilement les packages nécessaires. Commençons.

Créer un squelette

Notre application gérera les personnes qui souhaitent faire don de cadeaux, ou de choses dont elles n'ont tout simplement plus besoin, à quelqu'un qui pourrait en avoir besoin. Ainsi, les utilisateurs seront les donateurs et les bénéficiaires. Un Donateur peut créer un nouveau cadeau et voir la liste des cadeaux. Un destinataire peut voir la liste des cadeaux de tous les utilisateurs et peut réclamer ceux qui ne sont pas réclamés. Bien sûr, nous pourrions créer des donateurs et des destinataires en tant que rôles distincts sur la même entité (utilisateur), mais essayons de les séparer afin de voir comment créer des relations dans LoopBack. Le nom de cette application révolutionnaire sera Givesomebody .

Installez les outils de ligne de commande StrongLoop via npm :

 $ npm install -g strongloop

Exécutez ensuite le générateur d'applications de LoopBack :

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

Ajoutons un modèle. Notre premier modèle s'appellera Gift. LoopBack demandera la source de données et la classe de base. Comme nous n'avons pas encore configuré la source de données, nous pouvons mettre db (memory) . La classe de base est une classe de modèle générée automatiquement, et nous voulons utiliser PersistedModel dans ce cas, car elle contient déjà toutes les méthodes CRUD habituelles pour nous. Ensuite, LoopBack demande s'il doit exposer le modèle via REST (oui) et le nom du service REST. Appuyez sur Entrée ici pour utiliser la valeur par défaut, qui est simplement le pluriel du nom du modèle (dans notre cas, 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):

Enfin, nous donnons les noms des propriétés, leurs types de données et les drapeaux requis/non requis. Le cadeau aura les propriétés de name et de 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

Entrez un nom de propriété vide pour indiquer que vous avez terminé de définir les propriétés.

Le générateur de modèle créera deux fichiers qui définissent le modèle dans les common/models de l'application : gift.json et gift.js . Le fichier JSON spécifie toutes les métadonnées sur l'entité : propriétés, relations, validations, rôles et noms de méthode. Le fichier JavaScript est utilisé pour définir un comportement supplémentaire et pour spécifier les crochets distants à appeler avant ou après certaines opérations (par exemple, créer, mettre à jour ou supprimer).

Les deux autres entités modèles seront nos modèles de donneur et de récepteur. Nous pouvons les créer en utilisant le même processus, sauf que cette fois, mettons User comme classe de base. Cela nous donnera des propriétés telles que nom d' username , mot de password , e- email prêt à l'emploi. Nous pouvons ajouter juste le nom et le pays, par exemple, pour avoir une entité complète. Pour le destinataire, nous voulons également ajouter l'adresse de livraison.

Structure du projet

Examinons la structure de projet générée :

Structuration du projet

Les trois répertoires principaux sont : - /server – Contient les scripts d'application des nœuds et les fichiers de configuration. - /client - Contient .js, .html, .css et tous les autres fichiers statiques. - /common – Ce dossier est commun au serveur et au client. Les fichiers modèles vont ici.

Voici une ventilation détaillée du contenu de chaque répertoire, extraite de la documentation de LoopBack :

Fichier ou répertoire La description Comment accéder en code
Répertoire d'applications de niveau supérieur
package.json Spécification standard du package npm. Voir package.json N / A
/répertoire serveur - Fichiers d'application de nœud
server.js Fichier principal du programme d'application. N / A
config.json Paramètres de l'application. Voir config.json. app.get('setting-name')
datasources.json Fichier de configuration de la source de données. Voir datasources.json. Pour un exemple, voir Créer une nouvelle source de données . app.datasources['datasource-name']
model-config.json Fichier de configuration du modèle. Voir modèle-config.json. Pour plus d'informations, voir Connexion des modèles aux sources de données . N / A
middleware.json Fichier de définition de middleware. Pour plus d'informations, voir Définition du middleware. N / A
/boot Ajoutez des scripts pour effectuer l'initialisation et la configuration. Voir les scripts de démarrage. Les scripts sont automatiquement exécutés dans l'ordre alphabétique.
/répertoire client - fichiers d'application client
README.md Les générateurs LoopBack créent un fichier README vide au format Markdown. N / A
Autre Ajoutez vos fichiers HTML, CSS, JavaScript client.
/ répertoire commun - fichiers d'application partagés
/models Fichiers de modèle personnalisés :
  • Fichiers JSON de définition de modèle, nommés par convention nom model-name .json ; par exemple customer.json .
  • Scripts de modèle personnalisés par convention nommés model-name .js ; par exemple, customer.js .
Pour plus d'informations, voir Fichier JSON de définition de modèle et Personnalisation des modèles.
Nœud:
myModel = app.models.myModelName

Établir des relations

Dans notre exemple, nous avons quelques relations importantes à modéliser. Un donateur peut donner plusieurs cadeaux, ce qui donne la relation Donateur a plusieurs cadeaux . Un destinataire peut également recevoir de nombreux cadeaux, nous avons donc également la relation Receiver has many Gift . De l'autre côté, Gift appartient à Donor , et peut également appartenir à Receiver si le Receiver choisit de l'accepter. Mettons cela dans le langage de 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

Notez qu'il n'y a pas de modèle traversant ; nous tenons juste la référence au Don.

Si nous répétons la procédure ci-dessus pour Receiver et ajoutons deux appartient aux relations de Gift, nous réaliserons notre conception de modèle sur le côté back-end. LoopBack met automatiquement à jour les fichiers JSON des modèles pour exprimer exactement ce que nous venons de faire à travers ces simples boîtes de dialogue :

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

Ajouter une source de données

Voyons maintenant comment attacher une source de données réelle pour stocker toutes nos données d'application. Pour les besoins de cet exemple, nous utiliserons MongoDB, mais LoopBack a des modules pour se connecter à Oracle, MySQL, PostgreSQL, Redis et SQL Server.

Tout d'abord, installez le connecteur :

 $ npm install --save loopback-connector-mongodb

Ensuite, ajoutez une source de données à votre projet :

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

L'étape suivante consiste à configurer votre source de données dans server/datasources.json . Utilisez cette configuration pour un serveur MongoDB local :

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

Enfin, ouvrez server/model-config.json et changez la datasource de données pour toutes les entités que nous voulons conserver dans la base de données en "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 } }

Test de votre API REST

Il est temps de voir ce que nous avons construit jusqu'à présent ! Nous utiliserons l'outil intégré génial, API Explorer , qui peut être utilisé en tant que client pour le service que nous venons de créer. Essayons de tester les appels d'API REST.

Dans une fenêtre séparée, démarrez MongoDB avec :

 $ mongod

Exécutez l'application avec :

 $ node .

Dans votre navigateur, accédez à http://localhost:3000/explorer/ . Vous pouvez voir vos entités avec la liste des opérations disponibles. Essayez d'ajouter un donateur avec un appel POST /Donors .

Tester votre API 2

Tester votre API 3

L'explorateur d'API est très intuitif ; sélectionnez l'une des méthodes exposées et le schéma de modèle correspondant s'affichera dans le coin inférieur droit. Dans la zone de texte des data , il est possible d'écrire une requête HTTP personnalisée. Une fois la demande remplie, cliquez sur le bouton "Essayer", et la réponse du serveur s'affichera ci-dessous.

Tester votre API 1

Authentification d'utilisateur

Comme mentionné ci-dessus, l'une des entités pré-construites avec LoopBack est la classe User. L'utilisateur possède des méthodes de connexion et de déconnexion et peut être lié à une entité AccessToken qui conserve le jeton de l'utilisateur spécifique. En fait, un système complet d'authentification des utilisateurs est prêt à l'emploi. Si nous essayons d'appeler /Donors/login via API Explorer , voici la réponse que nous obtenons :

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

L' id est en fait la valeur du AccessToken, généré et conservé automatiquement dans la base de données. Comme vous le voyez ici, il est possible de définir un jeton d'accès et de l'utiliser pour chaque requête ultérieure.

Authentification d'utilisateur

Méthodes à distance

Une méthode distante est une méthode statique d'un modèle, exposée sur un point de terminaison REST personnalisé. Les méthodes distantes peuvent être utilisées pour effectuer des opérations non fournies par l'API REST modèle standard de LoopBack.

Outre les méthodes CRUD prêtes à l'emploi, nous pouvons ajouter autant de méthodes personnalisées que nous le souhaitons. Tous doivent aller dans le fichier [model].js . Dans notre cas, ajoutons une méthode distante au modèle Gift pour vérifier si le cadeau est déjà réservé, et une pour lister tous les cadeaux qui ne sont pas réservés.

Tout d'abord, ajoutons une propriété supplémentaire au modèle appelée reserved . Ajoutez simplement ceci aux propriétés de gift.json :

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

La méthode distante dans gift.js devrait ressembler à ceci :

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

Ainsi, pour savoir si un cadeau particulier est disponible, le client peut désormais envoyer une requête POST à /api/Gifts/free , en passant l' id du cadeau en question.

Crochets à distance

Parfois, il est nécessaire d'exécuter une méthode avant ou après la méthode distante. Vous pouvez définir deux types de hooks distants :

  • beforeRemote() s'exécute avant la méthode distante.
  • afterRemote() s'exécute après la méthode distante.

Dans les deux cas, vous fournissez deux arguments : une chaîne qui correspond à la méthode distante à laquelle vous souhaitez "raccrocher" votre fonction, et la fonction de rappel. Une grande partie de la puissance des crochets distants réside dans le fait que la chaîne peut inclure des caractères génériques, elle est donc déclenchée par n'importe quelle méthode de correspondance.

Dans notre cas, définissons un crochet pour imprimer des informations sur la console chaque fois qu'un nouveau donateur est créé. Pour ce faire, ajoutons un crochet "before create" dans 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 requête est appelée avec le context donné, et le rappel next() dans le middleware (discuté ci-dessous) est appelé après l'exécution du crochet.

Contrôles d'accès

Les applications LoopBack accèdent aux données via des modèles. Contrôler l'accès aux données signifie donc définir des restrictions sur les modèles ; c'est-à-dire, spécifier qui ou quoi peut lire et écrire les données ou exécuter des méthodes sur les modèles. Les contrôles d'accès LoopBack sont déterminés par des listes de contrôle d'accès, ou ACL.

Autorisons les donateurs et les destinataires non connectés à voir les cadeaux, mais seuls les donateurs connectés peuvent les créer et les supprimer.

 $ slc loopback:acl

Pour commencer, refusons à tout le monde l'accès à tous les terminaux.

 ? 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

Ensuite, autorisez tout le monde à lire à partir des modèles de cadeaux :

 $ 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

Ensuite, nous souhaitons autoriser les utilisateurs authentifiés à créer des cadeaux :

 $ 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

Et enfin, permettons au propriétaire du cadeau d'apporter des modifications :

 $ 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

Maintenant, lorsque nous examinons gift.json , tout devrait être en place :

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

Une remarque importante ici : $authenticated est un rôle prédéfini qui correspond à tous les utilisateurs du système (donateurs et destinataires), mais nous souhaitons uniquement autoriser les donateurs à créer de nouveaux cadeaux. Par conséquent, nous avons besoin d'un rôle personnalisé. Comme Role est une entité de plus que nous sortons de la boîte, nous pouvons tirer parti de son appel API pour créer le rôle $authenticatedDonor dans la fonction de démarrage, puis modifier simplement pricipalId dans gift.json .

Il faudra créer un nouveau fichier, server/boot/script.js , et ajouter le code suivant :

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

L'entité RoleMapping mappe les rôles aux utilisateurs. Assurez-vous que Role et RoleMapping sont tous deux exposés via REST. Dans server/model-config.json , vérifiez que "public" est défini sur true pour l'entité Role. Ensuite, dans donor.js , nous pouvons écrire un crochet "avant création" qui userID l'ID utilisateur et l'ID de roleID dans l'appel de l'API POST RoleMapping.

Intergiciel

Le middleware contient des fonctions qui sont exécutées lorsqu'une demande est faite au point de terminaison REST. Comme LoopBack est basé sur Express, il utilise le middleware Express avec un concept supplémentaire, appelé "phases middleware". Les phases sont utilisées pour définir clairement l'ordre dans lequel les fonctions du middleware sont appelées.

Voici la liste des phases prédéfinies, telles que fournies dans la documentation LoopBack :

  1. initial - Le premier point auquel le middleware peut s'exécuter.
  2. session - Préparez l'objet de session.
  3. auth - Gérer l'authentification et l'autorisation.
  4. parse - Analyse le corps de la requête.
  5. routes - Routes HTTP implémentant la logique de votre application. Le middleware enregistré via l'API express app.use, app.route, app.get (et d'autres verbes HTTP) s'exécute au début de cette phase. Utilisez également cette phase pour les sous-applications telles que loopback/server/middleware/rest ou loopback-explorer.
  6. files - Servir des actifs statiques (les requêtes atteignent le système de fichiers ici).
  7. final - Traite les erreurs et les demandes d'URL inconnues.

Chaque phase comporte trois sous-phases. Par exemple, les sous-phases de la phase initiale sont :

  1. initiale : avant
  2. initiale
  3. initiale : après

Jetons un coup d'œil sur notre middleware.json par défaut :

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

Dans la phase initiale, nous appelons loopback.favicon() ( loopback#favicon est l'identifiant du middleware pour cet appel). Ensuite, les modules tiers npm compression et cors sont appelés (avec ou sans paramètres). Dans la phase finale, nous avons deux autres appels. urlNotFound est un appel LoopBack et errorhandler est un module tiers. Cet exemple devrait démontrer que de nombreux appels intégrés peuvent être utilisés, tout comme les modules npm externes. Et bien sûr, nous pouvons toujours créer notre propre middleware et les appeler via ce fichier JSON.

loopback-boot

Pour conclure, mentionnons un module qui exporte la fonction boot() qui initialise l'application. Dans server/server.js , vous trouverez le morceau de code suivant, qui démarre l'application :

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

Ce script recherchera dans le dossier server/boot et chargera tous les scripts qu'il y trouvera par ordre alphabétique. Ainsi, dans server/boot , nous pouvons spécifier n'importe quel script qui doit être exécuté au démarrage. Un exemple est explorer.js , qui exécute API Explorer , le client que nous avons utilisé pour tester notre API.

Vous avez le blues de la répétition ? Ne créez plus cette API Node à partir de zéro. Laissez LoopBack le faire !
Tweeter

Conclusion

Avant de vous quitter, je voudrais mentionner StrongLoop Arc, une interface utilisateur graphique qui peut être utilisée comme alternative aux outils de ligne de commande slc . Il comprend également des outils pour créer, profiler et surveiller les applications Node. Pour ceux qui ne sont pas fans de la ligne de commande, cela vaut vraiment la peine d'essayer. Cependant, StrongLoop Arc est sur le point d'être obsolète et sa fonctionnalité est en cours d'intégration dans IBM API Connect Developer Toolkit.

Conclusion

De manière générale, LoopBack peut vous faire économiser beaucoup de travail manuel car vous obtenez beaucoup de choses prêtes à l'emploi. Il vous permet de vous concentrer sur les problèmes spécifiques à l'application et la logique métier. Si votre application est basée sur des opérations CRUD et manipule des entités prédéfinies, si vous en avez marre de réécrire l'infrastructure d'authentification et d'autorisation de l'utilisateur alors que des tonnes de développeurs l'ont écrit avant vous, ou si vous souhaitez tirer parti de tous les avantages d'un excellent framework Web comme Express, puis la création de votre API REST avec LoopBack peut réaliser vos rêves. C'est un morceau de gâteau !