Construyendo una API REST sin servidor con Node.js y MongoDB

El movimiento de Serverless ha ganado un poco de impulso en los últimos meses. Todo el mundo parece estar hablando de eso. ¡Algunos incluso lo llamarían una revolución! Pero no nos emocionemos demasiado. No seas como yo Me entusiasman demasiado las cosas buenas como esta y empiezo a escribir artículos. Si esto es todo nuevo para ti, aquí hay una pieza que escribí hace un tiempo, explicando los conceptos básicos.

Un curso intensivo sobre Serverless con Node.js
Independientemente de su experiencia como desarrollador, es inevitable que haya escuchado el término Serverless en el último año. La palabra … hackernoon.com

En ese espíritu, el tiempo que he invertido en explorar lo que es razonable construir usando Serverless Architecture puede exceder lo que se considera saludable. Mi conclusión es que casi todo es elegible para ser construido sin servidor. La única pregunta que debe hacerse es si realmente la necesita. Lambdas son apátridas, lo que significa que todo el concepto de escribir el código del lado del servidor necesita aprenderse de nuevo.

¿Suena divertido? Sí, es para mí también. Recientemente publiqué un curso práctico sobre el uso de la arquitectura Serverless en la vida real. Vertí todos mis hallazgos y razones sensatas para usar Serverless en este curso. Me preguntaba "¿Por qué necesito Serverless?" Durante todo el proceso de creación. Puedes encontrar mis pensamientos a continuación.

JavaScript sin servidor por ejemplo [Video] – Video | Ahora solo $ 5
Conviértase diestro con demostraciones en vivo en el desarrollo web sin servidor www.packtpub.com

¿Por qué usar Serverless para API REST?

Por qué no? ¿Es porque podemos, o vemos una clara ventaja sobre los servidores tradicionales? Ambos lados de la moneda tienen argumentos válidos. Serverless está concebido como siempre arriba. Como no tiene que administrar nada, no se preocupa por el tiempo de actividad, simplemente funcionará. También se escala automáticamente. Eso es bueno. Muy agradable. Escalar servidores no es divertido.

Pero, ¿qué pasa con el almacenamiento persistente? No podemos crear una base de datos MongoDB en un servidor como el que estamos acostumbrados. Sin embargo, si ha estado siguiendo el estilo de vida de "separación de preocupaciones" que ha ido en aumento en el último año, es posible que ya esté acostumbrado a separar su base de datos de su back-end. Aún más si estás acostumbrado a escribir microservicios. Simplemente le da a su aplicación una URL de conexión y allí está la base de datos, lista para funcionar.

¿Estás listo para un desafío?

Este artículo le mostrará cómo conectar una base de datos MongoDB como un servicio a una API REST sin servidor. Tal vez un poco descarado, ya que la forma preferida de usar AWS Serverless Architecture es con su NoSQL DBaaS llamado DynamoDB . Pero me gusta combinar cosas raras. Y, para ser sincero, MongoDB Atlas es increíble. Es el propio DBaaS de MongoDB. Puede obtener un clúster de MongoDB dedicado de forma gratuita.

Lo increíble con esta configuración es que te mostraré cómo escribir código de la forma en que ya estás acostumbrado. Todo lo que sabes al trabajar con Node.js, Express y Mongoose se volverá a utilizar en este tutorial.

Lo nuevo, es la mentalidad detrás de usar el servicio de cómputo Lambda . Una función AWS Lambda es básicamente un contenedor Docker . Una vez que se invoca el Lambda, el contenedor se activa y ejecuta el código. Esto es cuando queremos inicializar la conexión de la base de datos, la primera vez que se invoca la función, cuando el contenedor Docker se inicializa por primera vez. Cada solicitud posterior a la función Lambda debe usar la conexión de base de datos existente. ¿Suficientemente simple? ¡Vamos a crackear!

Comenzar a funcionar

Asumiré que ya tiene una comprensión básica del marco de Serverless. También espero que tenga una cuenta de AWS configurada. Si no lo hace, eche un vistazo al artículo que he vinculado en la parte superior .

1. Creando un servicio

Antes que nada, crearemos un nuevo servicio para mantener todo nuestro código.

 $ sls crea -t aws-nodejs -p rest-api && cd rest-api 

Este comando andamiará todos los archivos y códigos necesarios para crear nuestras funciones Lambda y eventos de Pasarela API. Hará esto en el camino que le dimos con la bandera -p . Lo que significa que creará un directorio llamado rest-api . Queremos cambiar en ese directorio y trabajar desde allí.

2. Instalación de módulos

Hay un par de módulos que necesitamos. En primer lugar, necesitamos el complemento Serverless Offline para poder ejecutar nuestro código localmente antes de implementarlo en AWS. Entonces tenemos que agarrar la mangosta , mi ORM de elección y dotenv , porque me gusta no presionar las teclas de GitHub. Al presionar las teclas de GitHub apesta. No hagas eso. Cada vez que presionas una tecla para GitHub, muere un pingüino bebé. Quiero decir, no realmente, pero aún así, es tan malo.

Asegúrate de estar en el directorio de rest-api . Primero instale Serverless Offline, luego mongoose y dotenv.

 $ npm init -y 
$ npm i --save-dev serverless-offline
$ npm i --save mongoose dotenv

Eso es todo, tomemos un descanso de la terminal y saltemos a Atlas para crear una base de datos.

3. Crear una base de datos en MongoDB Atlas

¿Listo para algo más de configuración? Sí, a nadie le gusta esta parte. Pero desnudo conmigo Salta a MongoDB Atlas e inscríbete.

MongoDB totalmente administrado, alojado en AWS, Azure y GCP
MongoDB Atlas es un servicio MongoDB alojado en la nube y diseñado y administrado por el mismo equipo que construye la base de datos. Es … www.mongodb.com

Es gratis y no se requiere tarjeta de crédito. Será la caja de arena que necesitamos para jugar. Una vez que haya configurado su cuenta, abra su página de cuenta y agregue una nueva organización.

Agrega un nombre que creas que se adapta, me quedaré con el rest-api . Presiona Siguiente y sigue y crea la organización.

Bonito. Eso lo llevará a la página de la organización. Presione el nuevo botón de proyecto.

Esto abrirá una página para nombrar su proyecto. Simplemente escriba " rest-api una vez más y presione "next".

A MongoDB le preocupan los permisos y la seguridad, por lo que Atlas le mostrará otra página de permisos de administración. Podemos omitir eso por el momento y crear el proyecto.

Uff, allí lo tenemos. ¡Finalmente, podemos crear el clúster real! Presione el gran botón verde "Crear un nuevo clúster" . Esto abrirá una enorme ventana de creación de clusters. Puede dejar todo predeterminado, solo asegúrese de elegir el tamaño de instancia M0 y deshabilitar las copias de seguridad.

Después de todo eso, solo agrega un usuario administrador para el clúster y dale una contraseña realmente sólida. Como puede ver, el precio de este clúster será de $ 0.00 / para siempre . Muy agradable. Eso es todo, presiona "Confirmar y desplegar" .

Su cluster tardará unos minutos en implementarse. Mientras eso está en marcha, finalmente comencemos a escribir un código.

Escribir un código

Esa configuración fue un puñado. Ahora tenemos que comenzar a escribir la configuración de recursos en el archivo serverless.yml y agregar los métodos CRUD reales al handler.js .

4. Configure todos los YAML

La maravilla del marco de Serverless radica en los grandes andamios iniciales. Puede crear una gran configuración usando solo el código comentado en el archivo serverless.yml . Pero, como soy un fanático de la limpieza, eliminemos todo y agreguemos el siguiente código. Después de copiarlo en su archivo serverless.yml voy a continuar y explicarlo todo.

Esta configuración es básica y lo suficiente para nuestras necesidades. Hemos establecido el tamaño de memoria máximo de Lambdas en 128 MB, que es más que suficiente para nuestras necesidades. Después de probarlos por mi cuenta durante un par de días, nunca superaron los 50 MB.

Vamos a lo interesante, la sección de funciones . Agregamos un total de 5 funciones: crear , obtener una , obtener todo , actualizar y eliminar . Todos apuntan a funciones exportadas de nombre idéntico en el archivo handler.js . Todos sus caminos siguen la convención de nomenclatura de una API REST estándar. Es increíble cómo es todo lo que necesitamos para configurar los recursos de API Gateway para activar nuestras funciones de Lambda.

Eso es más o menos, lo último es agregar una sección de complementos y serverless-offline . Instalamos este módulo arriba y lo usaremos para probar el servicio antes de implementarlo en AWS. Supongo que estamos listos para jugar con el handler.js siguiente. ¡Vamonos!

5. Completar las funciones

Estamos listos para divertirnos un poco ahora. Primero definiremos las 5 funciones que necesitamos y crearemos el diseño inicial del comportamiento que queremos. Después de eso, podemos crear la conexión de la base de datos y agregar la lógica de interacción de la base de datos con Mongoose.

En primer lugar abra el archivo handler.js . Verás la función hola predeterminada. Continúe y elimínelo todo y agregue el código a continuación.

Paso 1 de agregar lógica al handler.js

De acuerdo, está bien sentirse un poco abrumado. Pero, no hay necesidad de preocuparse. Estas son solo 5 funciones simples. Cada función tiene el mismo valor de context.callbackWaitsForEmptyEventLoop establecido en false , y comienza con la llamada a la función connectToDatabase() . Una vez que la función connectToDatabase() resuelve, continuará con la ejecución de la interacción de la base de datos a través de Mongoose. Usaremos los métodos del modelo Note para la interacción de la base de datos real. Pero espera, ¡no hemos definido ni creado nada de esto! Debes preguntarte qué pasa conmigo. Bueno, lo hice a propósito, primero quiero que veas que esto no es tan complicado ni diferente de crear una API REST con Node.js y Express.

Nota : context.callbackWaitsForEmptyEventLoop : de forma predeterminada, la devolución de llamada esperará hasta que el ciclo de eventos de tiempo de ejecución Node.js esté vacío antes de congelar el proceso y devolver los resultados a la persona que llama. Puede establecer esta propiedad en falso para solicitar que AWS Lambda congele el proceso poco después de que se llame a la callback llamada, incluso si hay eventos en el ciclo de evento. AWS Lambda congelará el proceso, cualquier dato de estado y los eventos en el ciclo de evento Node.js (cualquier evento restante en el ciclo de evento procesado cuando se llame a la función Lambda y si AWS Lambda elige usar el proceso congelado).
Documentación de AWS

Ha llegado el momento de agregar la conexión de la base de datos real. Lo que es importante entender antes de agregar el código es que la conexión se establecerá una vez. Cuando se invoca el Lambda por primera vez, lo que se conoce como arranque en frío, AWS activará un contenedor Docker para ejecutar el código. Esto es cuando nos conectamos a la base de datos. Todas las solicitudes posteriores utilizarán la conexión de base de datos existente. Conceptualmente, es bastante fácil de entender, pero un verdadero puñado cuando necesitamos guiarnos en el código. Aquí va.

6. Agregar la conexión a la base de datos

El proceso de conectarse a MongoDB es doble. Necesitamos crear una forma dinámica de crear la conexión pero también asegurarnos de volver a usar la misma conexión si está disponible. Comenzaremos lento.

Cree un nuevo archivo en el directorio raíz del servicio, justo al lado del handler.js . Dale un nombre bastante lógico de db.js y agrega el código a continuación.

Nota : Esta sintaxis es válida para Mongoose 5.0.0-rc0 y superior. No funcionará con ninguna versión de Mongoose que sea inferior a 5.

En la línea 1 estamos solicitando Mongoose, tal como estamos acostumbrados, y en la línea 2 agregando la biblioteca de promesas nativas para ser utilizada por Mongoose. Esto es porque queremos que los .then s funcionen correctamente en el handler.js cuando los llamamos con los métodos del modelo Note .

¿Y qué pasa con la variable isConnected ? Estamos creando un cierre, y el tratamiento se isConnected como el estado actual de la base de datos en el contenedor Docker en ejecución. Eche un vistazo a la función connectToDatabase que exportamos. En la línea 12 estamos estableciendo una conexión con una cadena de conexión que proporcionaremos a través de una variable de entorno. Esta función devuelve una promesa que simplemente hacemos .then y recuperamos un objeto db . Este objeto representa la conexión actual y tiene una propiedad de particular interés para nosotros. El .readyState nos dirá si existe una conexión o no. Si es así, será igual a 1 contrario es 0 .

Básicamente estamos almacenando en caché la conexión de la base de datos, asegurándonos de que no se cree si ya existe. En ese caso, solo resolvemos la promesa de inmediato.

Con el archivo db.js creado, vamos a requerirlo en el handler.js . Simplemente agregue este fragmento a la parte superior del controlador.

 // top of handler.js 
const connectToDatabase = require ('./ db');

7. Agregar un modelo de nota

Eche otro vistazo al handler.js. Puede ver que estamos llamando al modelo Note en las funciones para recuperar datos, pero no hay un modelo definido. Bueno, ahora es un momento tan bueno como cualquier otro.

Crear una nueva carpeta en el directorio raíz del servicio y el nombre de los modelos. En él, cree otro archivo y asígnele el nombre Note.js. Esto será solo un simple esquema de mangosta y definición de modelo.

Exportaremos el modelo en sí para que podamos usarlo en el handler.js . Eso es todo con respecto a la conectividad de la base de datos. Solo tenemos que agregar otra declaración de requerimiento en la parte superior del controlador y estamos listos para continuar.

 // top of handler.js 
const connectToDatabase = require ('./ db');
const Note = require ('./ models / Note');

Genial, ahora lo que queda es agregar una variable de entorno para contener nuestra URL de conexión de la base de datos MongoDB. Es una brisa con dotenv .

8. Uso de dotenv para variables de entorno

Dejar los archivos y las claves de configuración en un archivo totalmente separado es increíblemente fácil con dotenv y un verdadero salvavidas. Simplemente agregue el archivo a .gitignore y asegúrese de no arriesgarse a comprometer ninguna tecla. Deja que te enseñe.

Agregue un nuevo archivo, llámelo variables.env . Asegúrese de ponerlo en el directorio raíz del servicio. El archivo en sí solo tendrá una línea, y ese es el nombre de la variable de entorno junto con el valor. Debería verse algo así.

 DB = mongodb: // <usuario>: <contraseña> @ mongodb.net:27017/db 

Pero, primero tenemos que encontrar la URL de conexión. Para eso, tenemos que volver a Atlas. En la página principal de clústeres del proyecto que creó anteriormente, verá que se ha creado su clúster. Tiene un botón de conexión que queremos presionar.

Abrirá una nueva ventana emergente donde deberá agregar una dirección IP a la lista blanca, para que pueda acceder a la base de datos. Luego, toma la URL de conexión presionando el botón "Conectar su aplicación" .

Después de presionar "Conectar su aplicación", se le pedirá que " Copie una cadena de conexión" . Presione " Estoy usando el controlador 3.4 o anterior" y puede copiar FINALMENTE la URL. Whoa, ese fue un viaje tedioso.

Una vez que lo haya copiado, regrese al archivo variables.env y agregue la URL de conexión real.

 DB = mongodb: // dbadmin: reallystrongpassword@cluster0-shard-00-00-e9ai4.mongodb.net : 27017, cluster0-shard-00-01-e9ai4.mongodb.net: 27017, cluster0-shard-00-02- e9ai4.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin 

Asegúrese de no agregar espacios entre la base de datos y la URL de conexión. Cambie <password> por la contraseña que configuró anteriormente. El mío fue "reallystrongpassword". ¿Que pasará ahora? Bueno, las variables de este archivo se cargarán en el objeto process.env en Node.js, lo que significa que puede acceder a ellas de la manera estándar a la que ya está acostumbrado.

Nota : ¡No olvide agregar las variables.env al .gitignore!

Por último, antes de saltar a probar todo, necesitamos requerir el módulo dotenv y señalar el archivo donde guardamos las variables de entorno. Añada este fragmento a la parte superior de su archivo handler.js .

 require ('dotenv'). config ({ruta: './variables.env'}); 

Eso es. Es hora de probarlo.

¿Qué tal algunas pruebas?

Estamos listos para probar la API. En primer lugar, necesitamos ejecutar Serverless Offline. Pero, debido a la definición del modelo Mongoose que tenemos en Note.js, hay una bandera que debemos agregar al ejecutarla.

 $ sls fuera de línea de inicio --skipCacheInvalidation 

Nota : Debido a que Serverless Offline invalida el Node require cache en cada ejecución de manera predeterminada, agregamos este indicador para deshabilitarlo. En Node.js cuando require() un módulo, almacena una versión almacenada en caché del módulo, de modo que todas las llamadas posteriores a require() no tengan que volver a cargar el módulo desde el sistema de archivos.

Una vez que hayas ejecutado el comando en la terminal, deberías ver algo como esto.

Todas nuestras rutas están funcionando. Abra su cliente REST de elección, cartero, insomnio o lo que prefiera, y sigamos con las pruebas.

Utilizando Insomnia , he creado una solicitud POST para http://localhost:3000/notes con un cuerpo JSON.

Verificando el terminal se puede ver => using new database connection se registra, lo que significa que la conexión de la base de datos inicial se ha establecido. Envíe otra solicitud POST y verá => using existing database connection .

Impresionante, agregar una nueva nota funciona. Vamos a recuperar la nota que acabamos de agregar usando el método getOne . Copie el _id de la respuesta y péguelo en la URL de la solicitud GET.

Recuperar una sola nota también funciona bien. ¿Qué hay de recuperarlos a todos? Simplemente elimine el parámetro de ruta de ruta ID y presione "Enviar" una vez más.

Solo dos más para probar, editar y eliminar métodos. Elija uno de los _id s de las notas recuperadas y agréguelo nuevamente como parámetro de ruta. Ahora cambie el método a PUT y agregue un cuerpo JSON. Ingrese un título y una descripción diferente y presione "Enviar" .

La edición funciona bien, tal como queríamos. Solo la eliminación a la izquierda. Cambie al método DELETE, elimine el cuerpo de la solicitud y presione "Enviar" por última vez.

La nota fue eliminada exitosamente. Eso es más que suficiente con respecto a las pruebas. Estamos listos para implementar el servicio en AWS.

Ser responsable con el despliegue y la supervisión

Uf, eso es un montón de cosas que necesitas para entender. Estamos en la recta final. Lo único que queda es implementar el servicio y asegurarnos de que se comporta de la manera que queremos utilizando una herramienta de monitoreo llamada Dashbird .

9. Despliegue

El marco Serverless hace que las implementaciones sean rápidas y sencillas. Todo lo que necesitas hacer es ejecutar un comando.

 Despliegue $ sls 

Proporcionará automáticamente recursos en AWS, empaquetará y enviará todo el código a S3 desde donde se enviará a Lambdas. El terminal debería mostrar una salida similar a esta.

Nota : Puede repetir el proceso de prueba desde arriba con los puntos finales proporcionados.

Eso es todo lo que hay para el proceso de implementación. Fácil, ¿verdad? Esta es la razón por la que amo mucho el framework Serverless.

10. Monitoreo

Vamos a envolver esto con otra herramienta genial. Superviso mi Lambdas con Dashbird , y me encanta. Mi punto para mostrarte esto es que también veas los registros de la consola de las invocaciones de la función Lambda. Le mostrarán cuándo el Lambda está usando una conexión de base de datos nueva o existente. Así es como se ve el tablero principal, donde veo todas mis Lambdas y sus estadísticas.

Luego de presionar la función rest-api-dev-getAll Lambda, me llevarán a una pantalla con todas las estadísticas y registros de esta función en particular.

En la parte inferior, verá dos invocación de la función getAll. Después de presionar el más antiguo de los dos, me lleva a otra página que muestra información sobre esa invocación en particular.

Como puede ver, la consola se registró con => using new database connection y la solicitud real tomó aproximadamente 1,5 segundos.

Retrocediendo y presionando en la otra invocación, podemos ver una imagen similar pero, afortunadamente para nosotros, una imagen diferente.

Una vez que se ha invocado nuevamente la misma función Lambda, reutilizará la conexión existente. Se puede ver claramente en los registros aquí.

Final de la línea

Qué montaña rusa emocional. Te han embarcado en un viaje para crear una API REST sin servidor con MongoDB. Hice mi mejor esfuerzo para transferir la experiencia que he reunido hasta el día de hoy para mostrarte la forma preferida de crear una API adecuada. Muchas de las técnicas que he mostrado son las que utilizo a diario. Utilice estas habilidades sabiamente y disfrute de profundizar en las posibilidades de Serverless Architecture y todo lo que conlleva.

Si desea ver todo el código que escribimos arriba, aquí está el repositorio . O si quieres leer mis últimos artículos, dirígete aquí.

Últimas historias escritas por Adnan Rahi? – Medium
Lea las últimas historias escritas por Adnan Rahi? en Medium. Ingeniero de software @bookvar_co. Coding educator @ ACADEMY387 … medium.com

Espero que hayan disfrutado tanto leyendo esto como yo disfruté escribiéndolo.
¿Crees que este tutorial será de ayuda para alguien? No dude en compartir Si te gustó, aplasta el aplauso para que otras personas lo vean aquí en Medium.