DEV Community

Cover image for Erros comuns ao usar Node.js em Lambda
Eduardo Rabelo
Eduardo Rabelo

Posted on

Erros comuns ao usar Node.js em Lambda

Já se passaram seis meses desde que a AWS Lambda adicionou suporte ao Node.js 8.10. Estou super feliz por finalmente poder utilizar async/awaitpoder, simplificando minhas funções do Lambda.

Enquanto isso, ajudei alguns clientes com seus projetos serverless Node.js 8.10. Percebi alguns erros recorrentes ao utilizar async/await que vou compartilhar abaixo.

Ainda utilizando callbacks

Muitas pessoas ainda estão utilizando callbacks em suas funções async:

module.exports.handler = async (event, context, cb) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  cb(null, response)
}

a alternativa mais simples seria:

module.exports.handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  return response
}

Esquecer de usar util.promisify

Antes do Node.js 8.10, o pacote Bluebird preenchia uma lacuna enorme. Ele fornece utilitários para converter funções baseadas em callback para Promises. Mas o módulo nativo util do Node.js 8.10 preencheu essa lacuna com a função ‌promisify.

Por exemplo, agora podemos transformar a função readFile do módulo ‌fs da seguinte maneira:

const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)

Não há mais necessidade de usar o Bluebird. Uma dependência a menos, o que ajuda a reduzir o tempo de inicialização frio (cold start) de nossas funções.

Código sequencial desnecessário

async/await permite escrever código assíncrono como se ele fosse síncrono, o que é incrível. Não precisamos lidar com callback hell!

Por outro lado, também podemos deixar passar uma vantagem de não executar tarefas simultaneamente, quando apropriado.

Veja o seguinte código como exemplo:

async function getFixturesAndTeam(teamId) {
  const fixtures = await fixtureModel.fetchAll()
  const team = await teamModel.fetch(teamId)
  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

Esta função é fácil de ler, mas sua implementação dificilmente é a ideal. teamModel.fetch não depende do resultado fixtureModel.fetchAll, então eles devem ser executados simultaneamente.

Aqui está como você pode melhorar:

async function getFixturesAndTeam(teamId) {
  const fixturesPromise = fixtureModel.fetchAll()
  const teamPromise = teamModel.fetch(teamId)

  const fixtures = await fixturesPromise
  const team = await teamPromise

  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

Nesta versão, ambos fixtureModel.fetchAll e teamModel.fetch são iniciados simultaneamente.

Você também precisa estar atento ao usar map com async/await. O exemplo seguinte chamará cada teamModel.fetch sequencialmente:

async function getTeams(teamIds) {
  const teams = _.map(teamIds, id => await teamModel.fetch(id))
  return teams
}

Ao invés disso, podemos escrever:

async function getTeams(teamIds) {
  const promises = _.map(teamIds, id => teamModel.fetch(id))
  const teams = await Promise.all(promises)
  return teams
}

No exemplo acima, mapeamos teamIds para um array de promises. Podemos então usar Promise.all para transformar esse array em uma única promise que retorna um array de equipes.

Nesse caso, teamModel.fetch é chamado simultaneamente e pode melhorar significativamente o tempo de execução.

async/await dentro de forEach

Este é um truque que pode pegar até desenvolvedores experientes em Node.js.

O problema é que código como esse não se comporta da maneira que você espera:

[ 1, 2, 3 ].forEach(async (x) => {
  await sleep(x)
  console.log(x)
})

console.log('all done.')

Quando você executar isso, você obterá a seguinte saída:

all done.

Você pode ver esses artigos (1, 2) para uma explicação mais detalhada sobre por que isso não funciona. Por enquanto, lembre-se de evitar usar async/await dentro de um forEach!

Esquecer de usar .promise() no AWS-SDK

Você sabia que os clientes do AWS SDK oferecem suporte a retornos de chamada com promises? Para usar async/await no AWS SDK, adicione .promise() aos métodos do cliente:

const AWS = require('aws-sdk')
const Lambda = new AWS.Lambda()

async function invokeLambda(functionName) {
  const req = {
    FunctionName: functionName,
    Payload: JSON.stringify({ message: 'hello world' })
  }
  await Lambda.invoke(req).promise()
}

Diga não aos callbacks, yay! 🎉

Finalizando

É isso aí! Esses são os erros mais comuns que podem ser evitados ao trabalhar com Node.js 8.10 em Lambda. Para obter mais dicas sobre como criar aplicativos serverless, escaláveis e prontos para produção, consulte o meu curso em vídeo! ;-)

Créditos ⭐️

Top comments (2)

Collapse
 
rchgonzaga profile image
Rafael Gonzaga

E quando o time sobe um express centro de uma lambida :D heheeheh?

Collapse
 
oieduardorabelo profile image
Eduardo Rabelo • Edited

fala Rafa! :)

escrevi sobre isso no post anterior: dev.to/oieduardorabelo/6-coisas-qu...

no artigo, a camada intermediária era Node/Express, simplesmente pela familiaridade ai começar a migração para serverless

mas não é necessário, e para uma execução mais rápida da sua Lambda, eu recomendo você podar suas dependências e delegar roteamento para o AWS API Gateway,

vou compartilhar mais sobre no futuro :)