Definir o manipulador de função do Lambda em TypeScript - AWS Lambda

Definir o manipulador de função do Lambda em TypeScript

O manipulador da função do Lambda é o método no código da função que processa eventos. Quando sua função é invocada, o Lambda executa o método do manipulador. A função é executada até que o manipulador retorne uma resposta, seja encerrado ou atinja o tempo limite.

Conceitos básicos do handler em TypeScript

exemplo Manipulador TypeScript

Essa função de exemplo registra o conteúdo do objeto de evento e retorna a localização dos logs. Observe o seguinte:

  • Antes de usar esse código em uma função do Lambda, você deve adicionar o pacote @types /aws-lambda como uma dependência de desenvolvimento. Esse pacote contém as definições de tipo para o Lambda. Quando @types/aws-lambda está instalado, a instrução import (import ... from 'aws-lambda') importa as definições de tipo. Ela não importa o pacote aws-lambda do NPM, que é uma ferramenta de terceiros não relacionada. Para obter mais informações, consulte aws-lambda no repositório DefinitelyTyped do GitHub.

  • O manipulador neste exemplo é um módulo ES e deve ser designado como tal em seu arquivo package.json ou usando a extensão de arquivo .mjs. Para obter mais informações, consulte Designar um manipulador de funções como módulo ES.

import { Handler } from 'aws-lambda'; export const handler: Handler = async (event, context) => { console.log('EVENT: \n' + JSON.stringify(event, null, 2)); return context.logStreamName; };

O runtime transmite argumentos para o método do handler. O primeiro argumento é o objeto event, que contém informações do chamador. O invocador passa essas informações como uma string no formato JSON ao chamar Invoke, e o runtime as converte em um objeto. Quando um serviço da AWS invoca a sua função, a estrutura do evento varia de acordo com o serviço. Com o TypeScript, recomendamos o uso de anotações de tipo para o objeto de evento. Para ter mais informações, consulte Usar tipos para o objeto de evento.

O segundo argumento é o objeto de contexto, que contém informações sobre a invocação, a função e o ambiente de execução. No exemplo anterior, a função obtém o nome do fluxo de logs do objeto de contexto e o retorna para o invocador.

Também é possível usar um argumento de retorno de chamada, que é uma função que você pode chamar nos manipuladores não assíncronos para enviar uma resposta. Recomendamos o uso de async/await em vez dos retornos de chamada. Async/await oferece melhor legibilidade, tratamento de erros e eficiência. Para obter mais informações sobre as diferenças entre async/await e retornos de chamada, consulte Usar retornos de chamada.

Usar async/await

Se o código realizar uma tarefa assíncrona, use o padrão async/await para garantir que a execução do manipulador seja finalizada. Async/await é uma forma concisa e legível de escrever código assíncrono em Node.js, sem a necessidade de retornos de chamada aninhados ou promessas de encadeamento. Com async/await, é possível escrever um código que seja lido como código síncrono e, ao mesmo tempo, seja assíncrono e sem bloqueio.

A palavra-chave async marca uma função como assíncrona, e a palavra-chave await pausa a execução da função até que uma Promise seja resolvida.

exemplo Função TypeScript: assíncrona

Este exemplo usa fetch, que está disponível no runtime nodejs18.x. Observe o seguinte:

  • Antes de usar esse código em uma função do Lambda, você deve adicionar o pacote @types /aws-lambda como uma dependência de desenvolvimento. Esse pacote contém as definições de tipo para o Lambda. Quando @types/aws-lambda está instalado, a instrução import (import ... from 'aws-lambda') importa as definições de tipo. Ela não importa o pacote aws-lambda do NPM, que é uma ferramenta de terceiros não relacionada. Para obter mais informações, consulte aws-lambda no repositório DefinitelyTyped do GitHub.

  • O manipulador neste exemplo é um módulo ES e deve ser designado como tal em seu arquivo package.json ou usando a extensão de arquivo .mjs. Para obter mais informações, consulte Designar um manipulador de funções como módulo ES.

import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; const url = 'https://aws.amazon.com/'; export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => { try { // fetch is available with Node.js 18 const res = await fetch(url); return { statusCode: res.status, body: JSON.stringify({ message: await res.text(), }), }; } catch (err) { console.log(err); return { statusCode: 500, body: JSON.stringify({ message: 'some error happened', }), }; } };

Usar retornos de chamada

Recomendamos usar async/await para declarar o manipulador da função em vez de usar retornos de chamada. Async/await é a melhor opção por vários motivos:

  • Legibilidade: o código async/await é mais fácil de ler e entender do que o código de retorno de chamada, que pode logo se tornar difícil de seguir e transformar o retorno de chamada em um tormento.

  • Depuração e tratamento de erros: pode ser difícil depurar código baseado em retorno de chamada. A pilha de chamadas pode se tornar difícil de acompanhar, e os erros podem ser facilmente ignorados. Com async/await, você pode usar blocos try/catch para manipular erros.

  • Eficiência: retornos de chamada muitas vezes exigem alternância entre diferentes partes do código. O async/await pode reduzir o número de alternâncias de contexto, resultando em um código mais eficiente.

Quando você usa retornos de chamada no manipulador, a função continua em execução até que o loop de evento esteja vazio ou o tempo da função se esgote. A resposta não é enviada para o chamador até que todas as tarefas de loop de evento estejam concluídas. Se a função expirar, um erro será retornado. É possível configurar o runtime para enviar a resposta imediatamente, definindo context.callbackWaitsForEmptyEventLoop como false.

A função de retorno de chamada usa dois argumentos: um Error e uma resposta. O objeto de resposta deve ser compatível com JSON.stringify.

exemplo Função TypeScript com retorno de chamada

Essa função de exemplo recebe um evento do Amazon API Gateway, registra o evento e os objetos de contexto e, em seguida, retorna uma resposta ao API Gateway. Observe o seguinte:

  • Antes de usar esse código em uma função do Lambda, você deve adicionar o pacote @types /aws-lambda como uma dependência de desenvolvimento. Esse pacote contém as definições de tipo para o Lambda. Quando @types/aws-lambda está instalado, a instrução import (import ... from 'aws-lambda') importa as definições de tipo. Ela não importa o pacote aws-lambda do NPM, que é uma ferramenta de terceiros não relacionada. Para obter mais informações, consulte aws-lambda no repositório DefinitelyTyped do GitHub.

  • O manipulador neste exemplo é um módulo ES e deve ser designado como tal em seu arquivo package.json ou usando a extensão de arquivo .mjs. Para obter mais informações, consulte Designar um manipulador de funções como módulo ES.

import { Context, APIGatewayProxyCallback, APIGatewayEvent } from 'aws-lambda'; export const lambdaHandler = (event: APIGatewayEvent, context: Context, callback: APIGatewayProxyCallback): void => { console.log(`Event: ${JSON.stringify(event, null, 2)}`); console.log(`Context: ${JSON.stringify(context, null, 2)}`); callback(null, { statusCode: 200, body: JSON.stringify({ message: 'hello world', }), }); };

Usar tipos para o objeto de evento

Recomendamos não usar qualquer tipo para os argumentos do manipulador e tipo de retorno, pois você perde a capacidade de verificar tipos. Em vez disso, gere um evento usando o comando da CLI evento gerador local sam do AWS Serverless Application Model ou use uma definição de código aberto do pacote @types/aws-lambda.

Gerar um evento usando o comando sam local generate-event
  1. Gerar um evento proxy do Amazon Simple Storage Service (Amazon S3).

    sam local generate-event s3 put >> S3PutEvent.json
  2. Usar o utilitário quicktype para gerar definições de tipo do arquivo S3PutEvent.json.

    npm install -g quicktype quicktype S3PutEvent.json -o S3PutEvent.ts
  3. Use os tipos gerados no código.

    import { S3PutEvent } from './S3PutEvent'; export const lambdaHandler = async (event: S3PutEvent): Promise<void> => { event.Records.map((record) => console.log(record.s3.object.key)); };
Gerar um evento usando uma definição de código aberto do pacote @types/aws-lambda
  1. Adicione o pacote @types/aws-lambda como uma dependência de desenvolvimento.

    npm install -D @types/aws-lambda
  2. Use os tipos no código.

    import { S3Event } from "aws-lambda"; export const lambdaHandler = async (event: S3Event): Promise<void> => { event.Records.map((record) => console.log(record.s3.object.key)); };

Práticas recomendadas de código para as funções do Lambda em TypeScript

Adote as diretrizes da lista a seguir para usar as práticas recomendadas de codificação ao compilar suas funções do Lambda:

  • Separe o manipulador do Lambda da lógica central. Isso permite que você crie uma função mais fácil para teste de unidade. No Node.js isso pode ser semelhante a:

    exports.myHandler = function(event, context, callback) { var foo = event.foo; var bar = event.bar; var result = MyLambdaFunction (foo, bar); callback(null, result); } function MyLambdaFunction (foo, bar) { // MyLambdaFunction logic here }
  • Controle as dependências no pacote de implantação da função. O ambiente de execução do AWS Lambda contém várias bibliotecas. Para os runtimes em Node.js e Python, os AWS SDKs estão incluídos. Para habilitar o conjunto de recursos e atualizações de segurança mais recente, o Lambda atualizará periodicamente essas bibliotecas. Essas atualizações podem introduzir alterações sutis ao comportamento de sua função do Lambda. Para ter controle total das dependências usadas por sua função, empacote todas as dependências em seu pacote de implantação.

  • Minimize a complexidade de suas dependências. Prefira frameworks mais simples que sejam carregados rapidamente no startup do ambiente de execução.

  • Minimize o tamanho do pacote de implantação às necessidades do runtime. Isso reduzirá a quantidade de tempo necessária para que seu pacote de implantação seja obtido por download e desempacotado antes da invocação.

  • Aproveite a reutilização do ambiente de execução para melhorar a performance da função. Inicialize clientes SDK e conexões de banco de dados fora do manipulador de funções e armazene em cache os ativos estáticos localmente no diretório /tmp. As invocações subsequentes processadas pela mesma instância da função podem reutilizar esses recursos. Isso economiza custos reduzindo o runtime da função.

    Para evitar possíveis vazamentos de dados entre invocações, não use o ambiente de execução para armazenar dados do usuário, eventos ou outras informações com implicações de segurança. Se sua função depende de um estado mutável que não pode ser armazenado na memória dentro do manipulador, considere criar uma função separada ou versões separadas de uma função para cada usuário.

  • Use uma diretiva de keep-alive para manter conexões persistentes. O Lambda limpa conexões ociosas ao longo do tempo. A tentativa de reutilizar uma conexão ociosa ao invocar uma função resultará em um erro de conexão. Para manter sua conexão persistente, use a diretiva keep-alive associada ao runtime. Para obter um exemplo, consulte Reutilizar conexões com keep-alive em Node.js.

  • Use variáveis de ambiente para passar parâmetros operacionais para sua função. Por exemplo, se estiver gravando em um bucket do Amazon S3, em vez fixar no código o nome do bucket em que você está gravando, configure o nome do bucket como uma variável de ambiente.

  • Evite usar invocações recursivas em sua função do Lambda, em que a função invoca a si mesma ou inicia um processo que pode invocar a função novamente. Isso pode levar a um volume não intencional de invocações da função e a custos elevados. Se você observar um volume não intencional de invocações, defina a simultaneidade reservada da função como 0 imediatamente para limitar todas as invocações da função enquanto atualiza o código.

  • Não use APIs não documentadas e não públicas no código da função Lambda. Para os tempos de execução gerenciados pelo AWS Lambda, o Lambda aplica periodicamente atualizações funcionais e de segurança às APIs internas do Lambda. Essas atualizações internas da API podem ser incompatíveis com versões anteriores, gerando consequências não intencionais, como falhas de invocação, caso sua função tenha dependência nessas APIs não públicas. Consulte a referência da API para obter uma lista de APIs disponíveis publicamente.

  • Escreva um código idempotente. Escrever um código idempotente para suas funções garante que eventos duplicados sejam tratados da mesma maneira. Seu código deve validar eventos adequadamente e lidar corretamente com eventos duplicados. Para obter mais informações, consulte Como torno minha função do Lambda idempotente?.