Erros comuns ao usar Node.js em Lambda
Tags: ptbr, serverless, javascript, webdev
Published: 2019-05-20 13:50:47

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](https://serverless.com/blog/aws-lambda-node-8-support-what-changes-serverless-developers/).

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](http://bluebirdjs.com/docs/getting-started.html) 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](https://medium.com/@oieduardorabelo/javascript-armadilhas-do-asyn-await-em-loops-1cdad44db7f0), [2](https://medium.com/@oieduardorabelo/a-única-coisa-ruim-sobre-async-await-do-es7-413b485a7709)) 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](https://bit.ly/production-ready-serverless)! ;-)

# Créditos ⭐️

- [Common Node8 mistakes in Lambda](https://serverless.com/blog/common-node8-mistakes-in-lambda/), escrito originalmente por [Yan Cui](https://twitter.com/theburningmonk)