Definir el controlador de las funciones de Lambda en Node.js
El controlador de la función de Lambda es el método del código de la función que procesa eventos. Cuando se invoca una función, Lambda ejecuta el método del controlador. La función se ejecuta hasta que el controlador devuelve una respuesta, se cierra o se agota el tiempo de espera.
En esta página se describe cómo se trabaja con los controladores de funciones de Lambda en Node.js, incluidas las opciones para la configuración del proyecto, las convenciones de nomenclatura y las prácticas recomendadas. En esta página también se muestra un ejemplo de una función de Lambda de Node.js que recibe información sobre un pedido, genera un recibo en un archivo de texto y coloca este archivo en un bucket de Amazon Simple Storage Service (Amazon S3). Para obtener información sobre cómo implementar la función después de escribirla, consulte Implementar funciones Node.js de Lambda con archivos de archivo.zip o Implementar funciones Node.js Lambda con imágenes de contenedor.
Temas
Configuración del proyecto del controlador de Node.js
Existen varias formas de inicializar un proyecto de Lambda de Node.js. Por ejemplo, puede crear un proyecto de Node.js estándar mediante npm
, crear una aplicación de AWS SAM o crear una aplicación de AWS CDK.
Para crear un proyecto mediante npm
:
npm init
Este comando inicializa el proyecto y genera un archivo package.json
que gestiona los metadatos y las dependencias del proyecto.
El código de su función reside en un archivo de JavaScript .js
o .mjs
. En el siguiente ejemplo, nombramos este archivo index.mjs
porque utiliza un controlador de módulos de ES. Lambda admite tanto controladores de módulos de ES como de CommonJS. Para obtener más información, consulte Designación de un controlador de funciones como módulo de ES.
Un proyecto habitual de la función de Lambda en Node.js sigue esta estructura general:
/project-root ├── index.mjs — Contains main handler ├── package.json — Project metadata and dependencies ├── package-lock.json — Dependency lock file └── node_modules/ — Installed dependencies
Ejemplo de código de una función de Lambda en Node.js
El siguiente ejemplo de código de función de Lambda recibe información sobre un pedido, genera un recibo en un archivo de texto y coloca este archivo en un bucket de Amazon S3.
nota
En este ejemplo se usa un controlador de módulos de ES. Lambda admite tanto controladores de módulos de ES como de CommonJS. Para obtener más información, consulte Designación de un controlador de funciones como módulo de ES.
ejemplo Función de Lambda en index.mjs
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; // Initialize the S3 client outside the handler for reuse const s3Client = new S3Client(); /** * Lambda handler for processing orders and storing receipts in S3. * @param {Object} event - Input event containing order details * @param {string} event.order_id - The unique identifier for the order * @param {number} event.amount - The order amount * @param {string} event.item - The item purchased * @returns {Promise<string>} Success message */ export const handler = async(event) => { try { // Access environment variables const bucketName = process.env.RECEIPT_BUCKET; if (!bucketName) { throw new Error('RECEIPT_BUCKET environment variable is not set'); } // Create the receipt content and key destination const receiptContent = `OrderID: ${event.order_id}\nAmount: $${event.amount.toFixed(2)}\nItem: ${event.item}`; const key = `receipts/${event.order_id}.txt`; // Upload the receipt to S3 await uploadReceiptToS3(bucketName, key, receiptContent); console.log(`Successfully processed order ${event.order_id} and stored receipt in S3 bucket ${bucketName}`); return 'Success'; } catch (error) { console.error(`Failed to process order: ${error.message}`); throw error; } }; /** * Helper function to upload receipt to S3 * @param {string} bucketName - The S3 bucket name * @param {string} key - The S3 object key * @param {string} receiptContent - The content to upload * @returns {Promise<void>} */ async function uploadReceiptToS3(bucketName, key, receiptContent) { try { const command = new PutObjectCommand({ Bucket: bucketName, Key: key, Body: receiptContent }); await s3Client.send(command); } catch (error) { throw new Error(`Failed to upload receipt to S3: ${error.message}`); } }
Este archivo index.mjs
contiene las siguientes secciones de código:
-
Bloque
import
: use este bloque para incluir las bibliotecas que requiere la función de Lambda, como clientes de AWS SDK. -
Declaración
const s3Client
: esto inicializa un cliente de Amazon S3 fuera de la función del controlador. Esto hace que Lambda ejecute este código durante la fase de inicialización y el cliente se conserva para su reutilización en múltiples invocaciones. -
Bloque de comentarios de JSDoc: defina los tipos de entrada y salida de su controlador mediante anotaciones de JSDoc
. -
export const handler
: esta es la función principal del controlador que invoca Lambda. Al implementar su función, especifiqueindex.handler
para la propiedad Handler. El valor de la propiedadHandler
es el nombre del archivo y el nombre del método del controlador exportado, separados por un punto. -
Función
uploadReceiptToS3
: se trata de una función auxiliar a la que hace referencia la función del controlador principal.
Para que esta función se ejecute correctamente, su rol de ejecución debe permitir la acción s3:PutObject
. Además, asegúrese de definir la variable de entorno RECEIPT_BUCKET
. Tras una invocación correcta, el bucket de Amazon S3 debe contener un archivo de recibo.
Convenciones de nomenclatura de controladores
Al configurar una función, el valor de la configuración Handler es el nombre del archivo y el nombre del método del controlador exportado, separados por un punto. El valor predeterminado de las funciones creadas en la consola y de los ejemplos de esta guía es index.handler
. Esto indica el método handler
que se exporta desde el archivo index.js
o index.mjs
.
Si crea una función en la consola con un nombre de archivo o un nombre de controlador de funciones diferente, debe editar el nombre del controlador predeterminado.
Para cambiar el nombre de controlador de la función (consola)
-
Abra la página Funciones
de la consola de Lambda y elija su función. -
Elija la pestaña Código.
-
Desplácese hacia abajo hasta el panel Configuración del tiempo de ejecución y elija Editar.
-
En Controlador, ingrese el nuevo nombre del controlador de funciones.
-
Seleccione Guardar.
Definición del objeto de evento de entrada y acceso a él
El formato de entrada más común y estándar de las funciones de Lambda es JSON. En este ejemplo, la función espera una entrada similar a la siguiente:
{ "order_id": "12345", "amount": 199.99, "item": "Wireless Headphones" }
Al trabajar con funciones de Lambda en Node.js, puede definir la forma del evento de entrada mediante anotaciones de JSDoc. En este ejemplo, se define la estructura de entrada en el comentario de JSDoc del controlador:
/** * Lambda handler for processing orders and storing receipts in S3. * @param {Object} event - Input event containing order details * @param {string} event.order_id - The unique identifier for the order * @param {number} event.amount - The order amount * @param {string} event.item - The item purchased * @returns {Promise<string>} Success message */
Después de definir estos tipos en el comentario de JSDoc, puede acceder a los campos del objeto de evento directamente en el código. Por ejemplo, event.order_id
recupera el valor de order_id
de la entrada original.
Patrones de controlador válidos para funciones de Node.js
Le recomendamos que use async/await para declarar el controlador de funciones en lugar de usar devoluciones de llamadas. Async/await es una forma concisa y legible de escribir código asíncrono, sin necesidad de devoluciones de llamadas anidadas ni promesas encadenadas. Con async/await, puedes escribir código que se lea como código sincrónico, sin dejar de ser asíncrono y sin bloqueo.
Uso de async/await (recomendado)
La palabra clave async
marca una función como asíncrona y la palabra clave await
detiene la ejecución de la función hasta que se resuelva una Promise
. El controlador acepta los argumentos siguientes:
-
event
: contiene los datos de entrada que se pasan a la función. -
context
: contiene información sobre la función de invocación y el entorno de ejecución. Para obtener más información, consulte Uso del objeto de contexto Lambda para recuperar la información de la función Node.js.
A continuación se muestran las firmas válidas para el patrón async/await:
export const handler = async
(event)
=> { };export const handler = async
(event, context)
=> { };
nota
Use un entorno de desarrollo integrado (IDE) local o un editor de texto para escribir el código de la función de TypeScript. No se puede crear código de TypeScript en la consola de Lambda.
Uso de devolución de llamadas
Los controladores de devoluciones de llamada pueden usar los argumentos de evento, contexto y devolución de llamada. A continuación se muestran las firmas válidas:
export const handler =
(event, callback)
=> { };export const handler =
(event, context, callback)
=> { };
La función de devolución de llamada espera un Error
y una respuesta, que debe poder serializarse en JSON. La función continúa ejecutándose hasta que el bucle de eventos
ejemplo — solicitud HTTP con callback
La siguiente función de ejemplo comprueba una URL y devuelve el código de estado al invocador.
import https from "https"; let url = "https://aws.amazon.com/"; export const handler = (event, context, callback) => { https.get(url, (res) => { callback(null, res.statusCode); }).on("error", (e) => { callback(Error(e)); }); };
Uso de la versión 3 del SDK para JavaScript en el controlador
A menudo, utilizará las funciones de Lambda para interactuar con otros recursos de AWS o actualizarlos. La forma más sencilla de interactuar con estos recursos es utilizar AWS SDK para JavaScript. Todos los tiempos de ejecución de Node.js para Lambda admitidos incluyen la versión 3 del SDK para JavaScript. Sin embargo, se le recomienda firmemente que incluya los clientes de AWS SDK que sean necesarios en el paquete de implementación. Esto maximiza la compatibilidad con versiones anteriores en cualquiera de las futuras actualizaciones del tiempo de ejecución de Lambda. Solo confíe en el SDK proporcionado por el tiempo de ejecución cuando no pueda incluir paquetes adicionales (por ejemplo, cuando use el editor de código de la consola de Lambda o el código insertado en una plantilla de AWS CloudFormation).
Para agregar dependencias del SDK a la función, utilice el comando npm install
para los clientes del SDK específicos que necesite. En el código de ejemplo, se usa el cliente de Amazon S3. Agregue esta dependencia mediante la ejecución del siguiente comando en el directorio que contiene el archivo package.json
:
npm install @aws-sdk/client-s3
En el código de la función, importe el cliente y los comandos que sean necesarios, tal y como se demuestra en la función de ejemplo:
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
A continuación, inicialice un cliente de Amazon S3:
const s3Client = new S3Client();
En este ejemplo, inicializamos el cliente de Amazon S3 fuera de la función del controlador principal para evitar tener que inicializarlo cada vez que invocamos nuestra función. Después de inicializar el cliente del SDK, podrá usarlo para hacer llamadas a la API para ese servicio de AWS. El código de ejemplo llama a la API PutObject de Amazon S3 de la siguiente manera:
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
Body: receiptContent
});
Acceso a las variables de entorno
En el código del controlador, puede hacer referencia a cualquier variable de entorno mediante process.env
. En este ejemplo, se hace referencia a la variable de entorno RECEIPT_BUCKET
definida mediante las siguientes líneas de código:
// Access environment variables const bucketName = process.env.RECEIPT_BUCKET; if (!bucketName) { throw new Error('RECEIPT_BUCKET environment variable is not set'); }
Uso del estado global
Lambda ejecuta su código estático durante la fase de inicialización antes de invocar la función por primera vez. Los recursos que se crean durante la inicialización permanecen en la memoria entre las invocaciones, por lo que evita tener que crearlos cada vez que invoca la función.
En el código de ejemplo, el código de inicialización del cliente de S3 está fuera del controlador. El tiempo de ejecución inicializa el cliente antes de que la función gestione su primer evento; por su parte, el cliente permanece disponible para su reutilización en todas las invocaciones.
Prácticas recomendadas de codificación para las funciones de Lambda en Node.js
Siga estas directrices para desarrollar funciones de Lambda:
-
Separe el controlador de Lambda de la lógica del núcleo. Esto le permite probar las distintas unidades de la función con mayor facilidad.
-
Controle las dependencias del paquete de implementación de la función. El entorno de ejecución AWS Lambda contiene varias bibliotecas. Para los tiempos de ejecución de Node.js y Python, estos incluyen los AWS SDK. Para disponer del conjunto más reciente de características y actualizaciones de seguridad, Lambda actualizará periódicamente estas bibliotecas. Estas actualizaciones pueden introducir cambios sutiles en el comportamiento de la función de Lambda. Para disponer de un control total de las dependencias que utiliza la función, empaquete todas las dependencias con el paquete de implementación.
-
Minimice la complejidad de las dependencias. Son preferibles los marcos de trabajo más sencillos, ya que se cargan rápidamente al arrancar el entorno de ejecución.
-
Minimice el tamaño del paquete de implementación de acuerdo con las necesidades de su tiempo de ejecución. Esto reducirá la cantidad de tiempo que tarda el paquete de implementación en descargarse y desempaquetarse antes de la invocación.
-
Reutilice el entorno de ejecución para mejorar el rendimiento de la función. Inicialice los clientes de SDK y las conexiones de base de datos fuera del controlador de funciones y almacene localmente en caché los recursos estáticos en el directorio
/tmp
. Las invocaciones posteriores procesadas por la misma instancia de su función pueden reutilizar estos recursos. Esto ahorra costes al reducir el tiempo de ejecución de la función.Para evitar posibles filtraciones de datos entre las invocaciones, no utilice el entorno de ejecución para almacenar datos de usuario, eventos u otra información con implicaciones de seguridad. Si su función se basa en un estado mutable que no se puede almacenar en la memoria dentro del controlador, considere crear una función independiente o versiones independientes de una función para cada usuario.
-
Utilice una directiva keep-alive para mantener conexiones persistentes. Lambda purga las conexiones inactivas a lo largo del tiempo. Si intenta reutilizar una conexión inactiva al invocar una función, se producirá un error de conexión. Para mantener la conexión persistente, use la directiva keep-alive asociada al tiempo de ejecución. Para ver un ejemplo, consulte Reutilización de conexiones con Keep-Alive en Node.js.
-
Utilice variables de entorno para pasar parámetros operativos a su función. Por ejemplo, si está escribiendo en un bucket de Amazon S3, en lugar de codificar de forma rígida el nombre del bucket, configúrelo como una variable de entorno.
-
Evite utilizar invocaciones recursivas en la función de Lambda, en las que la función se invoca a sí misma o inicia un proceso que puede volver a invocarla. Esto podría producir un volumen no intencionado de invocaciones de la función y costos elevados. Si observa un volumen imprevisto de invocaciones, establezca la simultaneidad reservada de funciones en
0
inmediatamente para limitar todas las invocaciones de la función mientras actualiza el código. -
No utilice API no documentadas y no públicas en el código de la función de Lambda. Para tiempos de ejecución administrados de AWS Lambda, Lambda aplica periódicamente actualizaciones funcionales y de seguridad a las API internas de Lambda. Estas actualizaciones de las API internas pueden ser incompatibles con versiones anteriores, lo que conlleva consecuencias no deseadas, como errores de invocación si su función depende de estas API no públicas. Consulte la referencia de la API para obtener una lista de las API disponibles públicamente.
-
Escriba el código idempotente. Escribir el código idempotente para las funciones garantiza que los eventos duplicados se gestionen de la misma manera. El código debe validar y gestionar correctamente los eventos duplicados. Para obtener más información, consulte ¿Cómo puedo hacer que mi función de Lambda sea idempotente?
.