¿Cómo crear una función lambda con serverless framework en Node.js?
por Carlos Cardona y Guillermo Grajales, el 9 de septiembre de 2019
Esta es la continuación del tutorial para crear una aplicación serverless. Ya te hemos enseñado cómo preparar el entorno de desarrollo y cómo hospedar una página de Angular en S3. Ahora vamos a crear una función lambda con Node.js
El framework serverless que se instaló previamente se utiliza para crear y desplegar funciones lambda. Con dicho framework se pueden crear plantillas básicas de funciones lambda en diferentes lenguajes.
Para crear una función lambda para Node.js se utiliza el siguiente comando: serverless create -t aws-nodejs
El comando anterior creará un proyecto con la siguiente estructura:
El archivo handler.js contiene el código Javascript de la función lambda, mientras que en el archivo serverless.yml se encuentra toda la configuración relacionada con AWS.
Archivo serverless.yml
Inicialmente el archivo serverless.yml contiene el siguiente código:
Además del código anterior, la plantilla cuenta con diferentes configuraciones comentadas. En este tutorial explicamos las configuraciones principales, algunas opciones que están comentadas en la plantilla y otras que no aparecen en ella.
Para hacer que la explicación del archivo de configuración sea más fácil de entender, se supondrá que se tiene una función lambda que responderá a un evento http a través del método GET, la cual accede a una base de datos para obtener data de unos usuarios. A continuación, se muestra un archivo de configuración para tal escenario:
Del ejemplo anterior se pueden explicar a detalle algunos aspectos importantes:
- La función lambda debe poder acceder a la base de datos, DynamoDB en este caso, por ello se requiere que tenga un rol asociado, con la propiedad role. Como alternativa podría tener las políticas de acceso en la propiedad iamRoleStatements. En esta guía se explica la manera de hacerlo con la propiedad role.
- Debe establecerse en la configuración el evento HTTP, lo que incluye la URI, y el método de acceso. El handler de la función debe ser capaz de procesar correctamente los eventos que se configuren.
- La propiedad profile es importante cuando se tienen diferentes perfiles de acceso configurados, como se explicó en la sección 1.2.2, ya que es posible que algunos perfiles no tengan las políticas de acceso necesarias para desplegar la función lambda.
Para conocer más sobre las posibles configuraciones del archivo serverless.yml se puede visitar la siguiente página: info serverless.
Archivo handler.js
El contenido del archivo handler.js que proporciona la plantilla es bastante simple y se muestra a continuación:
El código anterior representa el handler más sencillo para responder a un evento de tipo HTTP. Puede verse que se está exportando la función con nombre hello, tal como aparece en el archivo serverless.yml de la plantilla. Esos nombres deben coincidir al asociar un handler a una función lambda.
La función que tiene el handler es bastante simple:
- Recibe dos parámetros cuando se dispara, uno que contiene información del evento (event), y el otro, información del contexto (context). Más adelante se dan detalles de cómo pueden ser útiles estos parámetros.
- Retorna un objeto de tipo key:value: dicho objeto tiene como estructura principal las siguientes keys:
- statusCode: el código HTTP dependiendo del tipo de respuesta (200 por defecto).
- body: contiene el cuerpo de la respuesta, generalmente de tipo JSON, por ello se ve en el código el uso de JSON.stringify.
- headers: contiene las cabeceras que tendrá la respuesta de la petición. En el código de la plantilla no se utiliza. Sin embargo, más adelante en esta guía se muestra la utilidad de esta key.
Despliegue de la función lambda
Una vez se ha especificado toda la configuración en el archivo serverless.yml y se ha desarrollado el código apropiado para un evento específico en el archivo handler.js, se puede desplegar la función lambda. Sin embargo, para lograr un despliegue exitoso con el framework serverless es necesario que el perfil de AWS con el que se está trabajando tenga las siguientes políticas de acceso:
- API Gateway: Full access
- CloudFormation: Full access
- CloudWatch Logs: Full access
- IAM: Full access
- Lambda: Full access
- S3: Full access
A continuación, se explica para qué son necesarios los permisos que se mencionaron anteriormente:
CloudFormation: el framework serverless convierte la configuración especificada en el archivo serverless.yml en lenguaje de CloudFormation y crea un stack con base en esa configuración.
CloudWatch Logs: serverless crea un grupo de logs en CloudWatch para almacenar los logs respectivos de la función lambda.
IAM: para poder asignar los roles y las políticas de acceso a las funciones es necesario poder acceder a IAM.
S3: el proceso de despliegue implica el almacenamiento de un archivo .zip que contiene los archivos de la función lambda. Dicho .zip se almacena en un bucket de S3.
Lambda: dado que se está creando una función lambda, se debe poder acceder a dicho servicio para crearla.
API Gateway: como se está creando una función lambda con eventos HTTP, serverless hace la integración entre la función lambda y el API Gateway automáticamente.
Una vez se hayan concedido los permisos necesarios al perfil con el cual se va a desplegar, se puede ejecutar el comando: serverless deploy, lo que da como resultado algo similar a lo que se muestra a continuación:
Al finalizar el despliegue de la función, se puede acceder desde el navegador a la URL proporcionada por Amazon y se debe obtener algo como lo siguiente:
Se observa el respectivo JSON, con las keys message e input.
En las consolas de S3, CloudFormation, Lambda y API Gateway, se puede verificar que se ha creado un elemento correspondiente a la función lambda que se ha desplegado. Todo esto lo hace automáticamente el framework serverless.
¿Cómo configurar el API Gateway?
A pesar de que serverless realiza la integración de la función lambda con el API Gateway, es necesario realizar una configuración adicional para que el front-end pueda consumir la función correctamente. Tal configuración se trata de CORS (Cross-Origin Resource Sharing), ya que generalmente el front-end se hospeda en un ambiente externo al de las funciones. A la hora de hacer las peticiones, el navegador rechazará el origen de la respuesta si en esta no se establecen unas cabeceras específicas.
El CORS se activa para un método HTTP de la función y se hace fácilmente desde la consola de API Gateway, a la cual se puede acceder a través del siguiente enlace: consola api gateway.
Como se ve en la imagen anterior, para la función hello, existe el método GET. Teniendo seleccionado dicho método, se debe pulsar sobre Actions, lo cual desprende una lista de opciones, tal como lo muestra la siguiente imagen:
Elige la opción Enable CORS. En la información que se muestra posteriormente, deja todo como viene por defecto y presiona Enable CORS and replace existing CORS headers. Luego, en la ventana que se abre, selecciona Yes, replace existing values. Con ese proceso habrá quedado activado el CORS para ese método; sin embargo, es necesario agregar unas cabeceras en la respuesta de la función lambda, tal como se describe a continuación.
En la sección 3.3 se mencionó la estructura típica de la respuesta de una función lambda con eventos HTTP. En dicha estructura se mostró que el key headers no se usaba en la plantilla que crea serverless. Ahora, en esta sección es necesario implementar dicha key para lograr que la página web pueda consumir las funciones lambda.
Para agregar los headers se recomienda hacer las siguientes modificaciones al código de ejemplo que entrega la plantilla.
Con las modificaciones realizadas, ya es posible consumir las funciones desde la aplicación de Angular, lo cual se muestra en detalle en la sección 5.
Roles en una función lambda
Un escenario típico en una aplicación serverless es aquel en el cual existe una aplicación web hospedada en un S3 bucket, una o varias funciones lambda que son llamadas por la aplicación web y distintos servicios de AWS. Generalmente las funciones lambda son las encargadas de acceder a los otros servicios de AWS, tales como DynamoDB, S3 Buckets, RDS, Rekognition, Logs, etc.
Las funciones lambda no podrán interactuar con otros servicios a menos que tengan asociado un rol que especifique las políticas de acceso.
¿Cómo crear un rol?
Los roles se crean en la consola de IAM. Al acceder a la sección de Roles, se pueden observar varios de ellos: tres son roles que proporciona Amazon, y debe existir otro que creado por serverless al desplegar la función lambda.
Para crear un rol, es necesario estar en la sección Roles de la consola de IAM y posteriormente hacer clic en el botón Create role.
En el primer paso, selecciona AWS service y Lambda, tal como se muestra en la imagen a continuación, luego presiona Next: Permissions.
En el paso de los permisos, escribe LambdaBasic en la barra de búsqueda y elige la política que aparece, llamada AWSLambdaBasicExecutionRole. Luego presiona el botón Create policy. En la nueva ventana que se abre, busca la pestaña JSON y escribe el siguiente código:
Acto seguido, presiona Review policy. En el nombre se puede poner DynamoDBFullAccess, ya que dicha política es para poder acceder a todas las funciones de DynamoDB. También se puede poner una descripción, de lo contrario se puede seleccionar Create policy.
Una vez hayas creado la política, en la barra de búsqueda de los permisos, escribe el nombre que se acaba de dar a la política y selecciónala. Luego, presiona Next: Review. Puedes darle un nombre al rol y cambiar la descripción que aparece por defecto. Finalmente selecciona Create role.
¿Cómo asignar un rol a una función lambda?
Existen dos maneras de asignar un rol existente a las funciones lambda. La primera opción es a través de la consola, y la segunda por medio del archivo serverless.yml. Idealmente es preferible tener toda la configuración en el archivo serverless.yml. Por eso en este tutorial te explicamos la segunda opción.
Ya te hemos mostrado cómo asignar un rol de manera global y de manera específica a una función. Dependiendo de las necesidades podrías necesitar una u otra forma. En este caso, se hará de manera global.
El atributo role en el archivo serverless.yml espera como valor el ARN de un rol existente. Lo anterior se obtiene desde la consola de IAM al seleccionar un rol determinado. En este caso, el rol que se debe elegir es el que se acaba de crear en la sección anterior. El ARN debe ser similar al que se muestra a continuación:
El valor que se obtenga de la consola de IAM debe ser asignado al atributo role en el archivo serverless.yml. Al hacerlo, el framework serverless asociará a la función lambda ese rol en el momento de desplegar.
En la próxima lección veremos cómo crear una base de datos DynamoDB.