Deje que LoopBack lo haga: un recorrido por el marco de la API de nodo con el que ha estado soñando

Publicado: 2022-03-11

No hace falta mencionar la creciente popularidad de Node.js para el desarrollo de aplicaciones. eBay ha estado ejecutando un servicio API de nodo de producción desde 2011. PayPal está reconstruyendo activamente su interfaz en Node. El sitio móvil de Walmart se ha convertido en la aplicación Node más grande, en términos de tráfico. El fin de semana de Acción de Gracias de 2014, los servidores de Walmart procesaron 1500 millones de solicitudes, el 70 % de las cuales se enviaron a través de dispositivos móviles y funcionaron con Node.js. Por el lado del desarrollo, el administrador de paquetes Node (npm) continúa creciendo rápidamente, superando recientemente los 150,000 módulos alojados.

Si bien Ruby tiene Rails y Python tiene Django, el marco de desarrollo de aplicaciones dominante para Node aún no se ha establecido. Pero hay un poderoso competidor que está ganando fuerza: LoopBack, un marco API de código abierto creado por la empresa StrongLoop de San Mateo, California. StrongLoop es un contribuyente importante a la última versión de Node, sin mencionar los mantenedores de Express desde hace mucho tiempo, uno de los marcos de Node más populares que existen.

Echemos un vistazo más de cerca a LoopBack y sus capacidades poniendo todo en práctica y creando una aplicación de ejemplo.

¿Qué es LoopBack y cómo funciona con Node?

LoopBack es un marco para crear API y conectarlas con fuentes de datos de back-end. Construido sobre Express, puede tomar una definición de modelo de datos y generar fácilmente una API REST integral completamente funcional a la que cualquier cliente puede llamar.

LoopBack viene con un cliente integrado, API Explorer . Usaremos esto porque hace que sea más fácil ver los resultados de nuestro trabajo y para que nuestro ejemplo pueda enfocarse en construir la API en sí.

Por supuesto, necesitará Node instalado en su máquina para seguir. Consiguelo aqui. npm viene con él, por lo que puede instalar fácilmente los paquetes necesarios. Empecemos.

crear un esqueleto

Nuestra aplicación administrará a las personas que deseen donar regalos, o cosas que ya no necesitan, a alguien que podría necesitarlas. Así, los usuarios serán Donantes y Receptores. Un Donante puede crear un nuevo regalo y ver la lista de regalos. Un Destinatario puede ver la lista de obsequios de todos los usuarios y puede reclamar los que no hayan sido reclamados. Por supuesto, podríamos construir Donantes y Receptores como roles separados en la misma entidad (Usuario), pero intentemos separarlos para que podamos ver cómo construir relaciones en LoopBack. El nombre de esta innovadora aplicación será Givesomebody .

Instale las herramientas de línea de comandos de StrongLoop a través de npm:

 $ npm install -g strongloop

Luego ejecute el generador de aplicaciones de LoopBack:

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

Agreguemos un modelo. Nuestro primer modelo se llamará Gift. LoopBack preguntará por la fuente de datos y la clase base. Como aún no hemos configurado la fuente de datos, podemos poner db (memory) . La clase base es una clase de modelo generada automáticamente, y queremos usar PersistedModel en este caso, ya que contiene todos los métodos CRUD habituales para nosotros. A continuación, LoopBack pregunta si debe exponer el modelo a través de REST (sí) y el nombre del servicio REST. Presiona enter aquí para usar el valor predeterminado, que es simplemente el plural del nombre del modelo (en nuestro 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):

Finalmente, damos los nombres de las propiedades, sus tipos de datos y las banderas requeridas/no requeridas. El regalo tendrá propiedades de name y 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

Introduzca un nombre de propiedad vacío para indicar que ha terminado de definir las propiedades.

El generador de modelos creará dos archivos que definen el modelo en common/models de la aplicación: gift.json y gift.js El archivo JSON especifica todos los metadatos sobre la entidad: propiedades, relaciones, validaciones, roles y nombres de métodos. El archivo JavaScript se usa para definir un comportamiento adicional y para especificar ganchos remotos que se llamarán antes o después de ciertas operaciones (por ejemplo, crear, actualizar o eliminar).

Las otras dos entidades modelo serán nuestros modelos Donante y Receptor. Podemos crearlos usando el mismo proceso, excepto que esta vez pondremos User como la clase base. Nos dará algunas propiedades como nombre de username , password , email de fábrica. Podemos agregar solo nombre y país, por ejemplo, para tener una entidad completa. Para el Destinatario también queremos agregar la dirección de entrega.

Estructura del proyecto

Echemos un vistazo a la estructura del proyecto generado:

Estructura del proyecto

Los tres directorios principales son: - /server : contiene archivos de configuración y secuencias de comandos de aplicación de nodo. - /client : contiene .js, .html, .css y todos los demás archivos estáticos. - /common : esta carpeta es común tanto para el servidor como para el cliente. Los archivos del modelo van aquí.

Aquí hay un desglose detallado del contenido de cada directorio, tomado de la documentación de LoopBack:

archivo o directorio Descripción Cómo acceder en código
Directorio de aplicaciones de nivel superior
package.json Especificación estándar del paquete npm. Ver paquete.json N / A
Directorio /servidor: archivos de aplicación de nodo
server.js Archivo de programa de aplicación principal. N / A
config.json Configuraciones de la aplicación. Consulte config.json. app.get('setting-name')
datasources.json Archivo de configuración de origen de datos. Consulte datasources.json. Para ver un ejemplo, consulte Crear una nueva fuente de datos . app.datasources['datasource-name']
model-config.json Archivo de configuración del modelo. Consulte model-config.json. Para más información, ver Conexión de modelos a fuentes de datos . N / A
middleware.json Archivo de definición de software intermedio. Para obtener más información, consulte Definición de middleware. N / A
/boot Agregue scripts para realizar la inicialización y la configuración. Ver secuencias de comandos de arranque. Los scripts se ejecutan automáticamente en orden alfabético.
/directorio cliente - archivos de la aplicación cliente
README.md Los generadores de LoopBack crean un archivo README vacío en formato Markdown. N / A
Otro Agregue sus archivos HTML, CSS, JavaScript del cliente.
/directorio común - archivos de aplicación compartidos
directorio /models Archivos de modelos personalizados:
  • Archivos JSON de definición de modelo, por convención denominados model-name .json ; por ejemplo customer.json .
  • Scripts de modelos personalizados por convención denominados model-name .js ; por ejemplo, customer.js .
Para obtener más información, consulte Archivo JSON de definición de modelo y Personalización de modelos.
Nodo:
myModel = app.models.myModelName

Construir relaciones

En nuestro ejemplo, tenemos algunas relaciones importantes para modelar. Un Donante puede donar muchos Dones, lo que da la relación Donante tiene muchos Dones. Un Receptor también puede recibir muchos Regalos, por lo que también tenemos la relación Receptor tiene muchos Regalos . Por otro lado, el Regalo pertenece al Donante y también puede pertenecer al Receptor si el Receptor decide aceptarlo. Pongamos esto en el lenguaje 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

Tenga en cuenta que no hay un modelo pasante; solo estamos sosteniendo la referencia al Don.

Si repetimos el procedimiento anterior para Receptor y agregamos dos relaciones pertenecientes a Regalo, lograremos el diseño de nuestro modelo en un lado posterior. LoopBack actualiza automáticamente los archivos JSON para que los modelos expresen exactamente lo que acabamos de hacer a través de estos simples diálogos:

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

Agregar una fuente de datos

Ahora veamos cómo adjuntar una fuente de datos real para almacenar todos los datos de nuestra aplicación. Para los propósitos de este ejemplo, usaremos MongoDB, pero LoopBack tiene módulos para conectarse con Oracle, MySQL, PostgreSQL, Redis y SQL Server.

Primero, instale el conector:

 $ npm install --save loopback-connector-mongodb

Luego, agregue una fuente de datos a su proyecto:

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

El siguiente paso es configurar su fuente de datos en server/datasources.json . Utilice esta configuración para un servidor MongoDB local:

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

Finalmente, abra server/model-config.json y cambie la fuente de datasource para todas las entidades que queremos que persistan en la base de datos a "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 } }

Prueba de su API REST

¡Es hora de ver lo que hemos construido hasta ahora! Usaremos la increíble herramienta integrada, API Explorer , que se puede usar como cliente para el servicio que acabamos de crear. Intentemos probar las llamadas a la API REST.

En una ventana separada, inicie MongoDB con:

 $ mongod

Ejecuta la aplicación con:

 $ node .

En su navegador, vaya a http://localhost:3000/explorer/ . Puedes ver tus entidades con la lista de operaciones disponibles. Intente agregar un Donante con una llamada POST /Donors .

Probando tu API 2

Probando tu API 3

API Explorer es muy intuitivo; seleccione cualquiera de los métodos expuestos y el esquema del modelo correspondiente se mostrará en la esquina inferior derecha. En el área de texto data , es posible escribir una solicitud HTTP personalizada. Una vez completada la solicitud, haga clic en el botón "Pruébelo" y la respuesta del servidor se mostrará a continuación.

Probando tu API 1

Autenticacion de usuario

Como se mencionó anteriormente, una de las entidades que viene prediseñada con LoopBack es la clase User. El usuario posee métodos de inicio y cierre de sesión, y puede vincularse a una entidad AccessToken que conserva el token del usuario específico. De hecho, un sistema completo de autenticación de usuarios está listo para usar. Si intentamos llamar a /Donors/login a través de API Explorer , esta es la respuesta que obtenemos:

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

La id es en realidad el valor del AccessToken, generado y persistente en la base de datos automáticamente. Como puede ver aquí, es posible configurar un token de acceso y usarlo para cada solicitud posterior.

Autenticacion de usuario

Métodos remotos

Un método remoto es un método estático de un modelo, expuesto sobre un punto final REST personalizado. Los métodos remotos se pueden utilizar para realizar operaciones no proporcionadas por la API REST del modelo estándar de LoopBack.

Además de los métodos CRUD que obtenemos de fábrica, podemos agregar tantos métodos personalizados como queramos. Todos ellos deben ir al archivo [model].js . En nuestro caso, agreguemos un método remoto al modelo Gift para verificar si el regalo ya está reservado y uno para enumerar todos los regalos que no están reservados.

Primero, agreguemos una propiedad adicional al modelo llamada reserved . Simplemente agregue esto a las propiedades en gift.json :

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

El método remoto en gift.js debería verse así:

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

Entonces, para averiguar si un regalo en particular está disponible, el cliente ahora puede enviar una solicitud POST a /api/Gifts/free , pasando la id del regalo en cuestión.

Ganchos remotos

A veces existe la necesidad de ejecutar algún método antes o después del método remoto. Puede definir dos tipos de ganchos remotos:

  • beforeRemote() se ejecuta antes que el método remoto.
  • afterRemote() se ejecuta después del método remoto.

En ambos casos, proporciona dos argumentos: una cadena que coincide con el método remoto al que desea "enganchar" su función y la función de devolución de llamada. Gran parte del poder de los enlaces remotos es que la cadena puede incluir comodines, por lo que se activa mediante cualquier método de coincidencia.

En nuestro caso, configuremos un enlace para imprimir información en la consola cada vez que se cree un nuevo Donante. Para lograr esto, agreguemos un gancho "antes de crear" 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 solicitud se llama con el context dado, y la devolución de llamada next() en el middleware (discutido a continuación) se llama después de que se ejecuta el gancho.

Controles de acceso

Las aplicaciones de LoopBack acceden a los datos a través de modelos, por lo que controlar el acceso a los datos significa definir restricciones en los modelos; es decir, especificar quién o qué puede leer y escribir los datos o ejecutar métodos en los modelos. Los controles de acceso de LoopBack están determinados por listas de control de acceso o ACL.

Permitamos que los Donantes y Receptores que no hayan iniciado sesión vean los obsequios, pero solo los Donantes que hayan iniciado sesión puedan crearlos y eliminarlos.

 $ slc loopback:acl

Para comenzar, neguemos a todos el acceso a todos los puntos finales.

 ? 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

A continuación, permita que todos lean los modelos de Gift:

 $ 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

Luego, queremos permitir que los usuarios autenticados creen regalos:

 $ 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

Y finalmente, permitamos que el dueño del regalo haga cualquier cambio:

 $ 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

Ahora, cuando gift.json , todo debería estar en su lugar:

 "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 aquí: $authenticated es un rol predefinido que corresponde a todos los usuarios en el sistema (tanto Donantes como Receptores), pero solo queremos permitir que los Donantes creen nuevos Regalos. Por lo tanto, necesitamos un rol personalizado. Como el rol es una entidad más que sacamos de la caja, podemos aprovechar su llamada a la API para crear el rol $authenticatedDonor en la función de arranque y luego simplemente modificar pricipalId en gift.json .

Será necesario crear un nuevo archivo, server/boot/script.js , y agregar el siguiente código:

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

La entidad RoleMapping asigna roles a usuarios. Asegúrese de que Role y RoleMapping estén expuestos a través de REST. En server/model-config.json , verifique que "public" esté establecido en true para la entidad Role. Luego, donor.js , podemos escribir un gancho "antes de crear" que asignará el ID de usuario y el ID de roleID en la llamada API POST de userID .

software intermedio

El middleware contiene funciones que se ejecutan cuando se realiza una solicitud al punto final REST. Como LoopBack se basa en Express, utiliza el middleware Express con un concepto adicional, llamado "fases de middleware". Las fases se utilizan para definir claramente el orden en que se llaman las funciones en el middleware.

Aquí está la lista de fases predefinidas, tal como se proporciona en los documentos de LoopBack:

  1. initial : el primer punto en el que se puede ejecutar el middleware.
  2. sesión : prepara el objeto de la sesión.
  3. auth - Manejar la autenticación y autorización.
  4. parse : analiza el cuerpo de la solicitud.
  5. rutas : rutas HTTP que implementan la lógica de su aplicación. El middleware registrado a través de Express API app.use, app.route, app.get (y otros verbos HTTP) se ejecuta al comienzo de esta fase. Utilice esta fase también para aplicaciones secundarias como loopback/server/middleware/rest o loopback-explorer.
  6. archivos : sirve activos estáticos (las solicitudes llegan al sistema de archivos aquí).
  7. final - Trate con errores y solicitudes de URL desconocidas.

Cada fase tiene tres subfases. Por ejemplo, las subfases de la fase inicial son:

  1. inicial: antes
  2. inicial
  3. inicial:después

Echemos un vistazo rápido a nuestro middleware.json predeterminado:

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

En la fase inicial, llamamos a loopback.favicon() ( loopback#favicon es la identificación del middleware para esa llamada). Luego, se llama a compression y cors de los módulos npm de terceros (con o sin parámetros). En la fase final, tenemos dos convocatorias más. urlNotFound es una llamada LoopBack y errorhandler es un módulo de terceros. Este ejemplo debería demostrar que muchas llamadas integradas se pueden usar como los módulos npm externos. Y por supuesto, siempre podemos crear nuestro propio middleware y llamarlo a través de este archivo JSON.

loopback-boot

Para terminar, mencionemos un módulo que exporta la función boot() que inicializa la aplicación. En server/server.js encontrará el siguiente fragmento de código, que arranca la aplicación:

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

Este script buscará en la carpeta de server/boot y cargará todos los scripts que encuentre allí en orden alfabético. Por lo tanto, en server/boot , podemos especificar cualquier script que deba ejecutarse al inicio. Un ejemplo es explorer.js , que ejecuta API Explorer , el cliente que usamos para probar nuestra API.

¿Tienes el blues de repetición? No vuelvas a crear esa API de nodo desde cero. ¡Deja que LoopBack lo haga!
Pío

Conclusión

Antes de dejarlo, me gustaría mencionar StrongLoop Arc, una interfaz de usuario gráfica que se puede usar como alternativa a las herramientas de línea de comandos de slc . También incluye herramientas para construir, perfilar y monitorear aplicaciones de Nodo. Para aquellos que no son fanáticos de la línea de comandos, definitivamente vale la pena intentarlo. Sin embargo, StrongLoop Arc está a punto de quedar obsoleto y su funcionalidad se está integrando en IBM API Connect Developer Toolkit.

Conclusión

En términos generales, LoopBack puede ahorrarle mucho trabajo manual, ya que obtiene muchas cosas listas para usar. Le permite centrarse en los problemas específicos de la aplicación y la lógica empresarial. Si su aplicación se basa en operaciones CRUD y en la manipulación de entidades predefinidas, si está harto de reescribir la infraestructura de autenticación y autorización del usuario cuando muchos desarrolladores lo han escrito antes que usted, o si desea aprovechar todas las ventajas de un gran marco web como Express, construir su API REST con LoopBack puede hacer realidad sus sueños. ¡Es un trozo de tarta!