Serverless Framework: Tratamento de erros no AWS Lambda e Amazon API Gateway

Image for post
Image for post

As ofertas da AWS Lambda e Amazon API Gateway forneceram um novo mecanismo poderoso para o desenvolvimento rápido de APIs REST sem a sobrecarga de criação de infraestrutura e código boilerplate para ativar servidores web. Quando você percebe que o SAMestá uma bagunça e passa para o Serverless Framework, as coisas realmente começam a voar. Inevitavelmente, você chegará ao ponto em que essa prova de conceito precisa começar a se tornar mais real. Isso significa adicionar funcionalidades como autenticação e tratamento/relatório de erros. Depois de vasculhar a web e achar os resultados insatisfatórios, decidi publicar minha experiência aqui na esperança de que outros possam se beneficiar.

Objetivo: fornecer códigos de status e mensagens apropriadas para os consumidores da API REST, como a interface do usuário em Angular, ao mesmo tempo em que fornecemos uma intrusão mínima dos códigos de status HTTP no próprio código Lambda.

Etapa 1: Integração Lambda Padrão

O Serverless Framework faz um ótimo trabalho na criação de recursos com padrões razoáveis, removendo a necessidade de código boilerplate, para que o desenvolvedor (ou engenheiro DevOps) possa se concentrar em “coisas mais importantes”. Para fazer sentido, a primeira coisa que precisamos fazer é tentar entender o que o Serverless Framework oferece por padrão.

Usando a Integração Lambda, criamos um endpointsimples em nosso serverless.yml:

Quando fazemos o deploy na AWS, vemos o seguinte no Amazon API Gateway:

Serverless Framework, por padrão, criou várias expressões regulares que são mapeadas para códigos de erro. Com isso, precisamos garantir que os erros lançados em nosso código incluam o código de status apropriado com a string de erro esperada. Vejamos um exemplo:

Fácil né?

Talvez não. O que acontece se recebermos um erro em createDeal() que não esteja em conformidade com este padrão? Não podemos confiar em todos os métodos e bibliotecas chamados para saber se as strings de erro precisam conter um código de status HTTP. Certamente, podemos executar um try..catch, mas isso ainda coloca a lógica de determinar o código de status HTTP em nosso código dentro do endpoint. Além disso, qualquer consumidor da resposta precisará usar ou analisar o código de status da mensagem de erro. Por fim, se um erro for gerado sem um dos códigos de status mapeados, o API Gateway retornará uma resposta 504 Bad Gateway, que é de pouco valor para o consumidor. Nós podemos fazer melhor.

Etapa 2: Integração Proxy Lambda

Uma recomendação comum ao criar Lambdas é usar a “integração de proxy”. O serverless-stack tem uma excelente descrição de como fazer isso; portanto, tentarei não ser repetitivo. O desenvolvedor é responsável por definir o código de status HTTP ao criar uma resposta e é responsável por capturar quaisquer erros no código, para que uma resposta apropriada possa ser criada. Vamos dar uma olhada nos exemplos abaixos:

Observe que, em vez de usar a integração lambda, estamos usando a integração padrão (proxy).

Essa abordagem tem seus prós e contras. Como a solução anterior, o desenvolvedor é responsável por capturar todos os erros e retornar o código e a mensagem de status apropriados. Também como a solução anterior, obteremos um 504 Bad Gateway se a resposta não estiver no formato correto, como quando um erro não detectado é gerado.

A solução no serverless-stack inclui uma função utilitária para simplificar a resposta return buildResponse(statusCode, body). Isso ajuda parte do código que é comum em torno da resposta, como informações do cabeçalho, mas não aborda as preocupações de erros não capturados e mapeamentos de código de status na função lambda.

Etapa 3: Middleware com Middy

Uma abordagem popular para abstrair muito do clichê de códigos Lambda é usar uma solução de middleware como o middy. Middy envolve a chamada lambda e permite ao desenvolvedor anexar qualquer número de código em pré e pós-execução, que possa ser usado para modificar a solicitação ou resposta. Isso não soluciona o problema de declarar códigos de status HTTP na função lambda, mas fornece alguns recursos poderosos para integração com outras bibliotecas comuns, como o http-errors. Infelizmente, o ganho líquido para este caso de uso não é melhor do que as soluções mais leves acima. Acabamos com um resultado semelhante com mais dependências e código adicional:

Novamente estamos usando a integração padrão (proxy).

O middleware CORS é uma adição bem-vinda, pois permite ignorar a entrada manual do cabeçalho 'Access-Control-Allow-Origin', mas precisamos adicionar o middleware de terceiros auto-proxy-responsepara agrupar o valor retornado em um formato de resposta lambda válido (com código de status 200) ou criar manualmente o objeto de resposta.

Etapa 4 — Final: Mapeando códigos de erro no API Gateway

Após muita discussão sobre usar a resposta do proxy (como recomendado pelo Serverless Framework e pela AWS), cheguei a uma conclusão que já vi repetidamente na web: tornar o desenvolvimento de curto prazo mais rápido, mas não fornece a separação que eu esperaria da resposta HTTP do código lambda. Essa separação pode ser usada para simplificar a vida dos desenvolvedores, dificultando o tiro no pé por não conseguir detectar um erro e ao mesmo tempo reduzir boilerplate para tornar o código mais legível como uma unidade de trabalho. O que isso adiciona é uma configuração extra no Serverless Framework na forma de modelos de resposta e regex de códigos de erros.

Código lambda:

Retornamos a um código lambda simples em um mundo onde os códigos de status HTTP são alegremente desconhecidos. Se createDeal() retornar um erro que não seja capturado e reconvertido, não interromperemos o tratamento pelo API Gateway.

No serverless.yml

Nosso error-response-template.yml:

E em 500-response-template.yml:

Toda a manipulação do código de status HTTP foi abstraída na configuração do API Gateway. O API Gateway aplicará o regex pattern no campo errorMessage da resposta para determinar qual código de status e modelo de resposta usar (consulte a documentação para mais detalhes). Nesse caso, configurei 3 códigos de status possíveis:

  • 200 — se não houver mensagem de erro
  • 400 — se a mensagem de erro começar com a sequência Missing
  • 500 — se a mensagem de erro iniciar com qualquer outra sequência (catch-all)

Eles funcionarão além dos códigos de status 401 e 403 existentes que o API Gateway retornará quando a autenticação ou autorização falhar usando um autorizador personalizado (tópico para outro artigo).

Além disso, criei modelos de resposta padrão que podem ser reutilizados, reduzindo o copiar e colar, padronizando o formato de retorno.

No mais, acredito que isso fornece a experiência mais simples para o desenvolvedor, maximizando o comportamento consistente e o formato de resposta. O desenvolvedor não precisa se preocupar com códigos de status HTTP ao escrever funções lambda, e os possíveis códigos de status que podem ser retornados são documentados em um único local, e não em todo o código. Há algum custo e complexidade em executar a regex na mensagem de erro, mas para esse caso de uso é uma troca aceitável.

Eu espero que você tenha achado útil essa explicação!

Créditos

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store