Configurando um ambiente Serverless Offline com NodeJS + Typescript no Mac M1

Há alguns dias escrevi um artigo aqui no TabNews mostrando como resolvi um problema que estava enfrentando para rodar um ambiente Serverless localmente num Mac M1. Tudo que fiz lá realmente funcionou... por um tempo. Risos.

Se quiser ler o artigo, é esse aqui. E caso queira pular direto pra como configurar o ambiente, basta ir até o tópico Resolução

Depois de seguir no projeto que estava fazendo, precisei utilizar algumas bibliotecas que começaram a gerar erros quando o Typescript era convertido para Javascript, entendi que era por conta da versão do Javascript que o plugin transpilava, o que impossibilitou o uso da solução que havia criado. Então, como nem tudo são flores, voltei a estaca zero e fui tentar resolver o erro que estava recebendo. Pra minha alegria, depois de algumas tentativas e erros, consegui resolver!! :confetti_ball:

Então nesse artigo vou direto ao ponto, pois encontrei uma solução concreta de como configurar um ambiente Serverless Offline com NodeJS + Typescript no Mac M1.

Vou seguir mais como um tutorial, se quiserem saber as tentativas até chegar a solução que vou descrever aqui, só comentar que faço uma continuação. :wink:

Antes de mais nada vou dar um contexto do que me aconteceu...

Como cheguei até o erro

Ao criar um projeto Serverless a partir do template aws-nodejs-typescript, juntamente com o plugin serverless-offline recebia um belo erro quando tentava iniciar a aplicação localmente. O passo a passo até o erro foi:

  • serverless create --template aws-nodejs-typescript --path serverless para criar o projeto
  • yarn para instalar as dependências
  • yarn add serverless-offline -D para instalar o plugin que permite rodar a aplicação localmente como dependência de desenvolvimento
  • Adicionei o plugin na lista de plugins do arquivo serverless.ts como na imagem abaixo: serverless.ts
  • Rodei o comando serverless offline e enfim o erro...
Error: 
You installed esbuild for another platform than the one you're currently using.
This won't work because esbuild is written with native code and needs to
install a platform-specific binary executable.

Specifically the "@esbuild/darwin-arm64" package is present but this platform
needs the "@esbuild/darwin-x64" package instead. People often get into this
situation by installing esbuild with npm running inside of Rosetta 2 and then
trying to use it with node running outside of Rosetta 2, or vice versa (Rosetta
2 is Apple's on-the-fly x86_64-to-arm64 translation service).

If you are installing with npm, you can try ensuring that both npm and node are
not running under Rosetta 2 and then reinstalling esbuild. This likely involves
changing how you installed npm and/or node. For example, installing node with
the universal installer here should work: https://nodejs.org/en/download/. Or
you could consider using yarn instead of npm which has built-in support for
installing a package on multiple platforms simultaneously.

If you are installing with yarn, you can try listing both "arm64" and "x64"
in your ".yarnrc.yml" file using the "supportedArchitectures" feature:
https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures
Keep in mind that this means multiple copies of esbuild will be present.

Another alternative is to use the "esbuild-wasm" package instead, which works
the same way on all platforms. But it comes with a heavy performance cost and
can sometimes be 10x slower than the "esbuild" package, so you may also not
want to do that.

Entendendo o problema

Se você estiver aqui depois de ter recebido o erro acima, é possível que tenha algumas pequenas diferenças entre o exemplo que coloquei e o erro recebido. Eu mesmo recebi o erro com algumas informações diferentes nas várias vezes que o vi. Mas isso não vai interferir na solução que cheguei.

Primeiro de tudo precisamos entender o porquê o erro aconteceu: O Esbuild tem um pacote auxiliar pra cada sistema e arquitetura (Linux, Darwin, Windows... e arm64, x64...), e o erro diz que temos instalada a versão esbuild-darwin-arm64, o ideal para o chip M1. Mas também diz que pra funcionar, ele precisa do pacote esbuild-darwin-x64, que seria ideal pra um chip Intel. O que nos leva a entender que por debaixo dos panos, entre o relacionamento entre o Node e o Esbuild, um está entendendo que a arquitetura é arm64e outro que é x64.

Pra confirmar isso podemos rodar o comando node -p process.arch, se retornar arm64, o Node está rodando na arquitetura do chip M1 e o Esbuild está, em algum momento, tentando rodar como se a arquitetura fosse x64.

Resolução

Dentre o vários erros similares que recebi, um é o que está descrito acima, que nos diz o seguinte:

People often get into this situation by installing esbuild with npm running inside
of Rosetta 2 and then trying to use it with node running outside of Rosetta 2, 
or vice versa (Rosetta 2 is Apple's on-the-fly x86_64-to-arm64 translation service).

A partir disso vem a solução, o Rosetta 2 serve para "simular" o funcionamento da arquitetura x64 dentro do arm64, então precisamos rodar todo o nosso sistema dentro do Rosetta 2, partiu configurar.

Precisamos iniciar um terminal utilizando o Rosetta 2:

  1. Abra o Finder > Aplicativos
  2. Clique com o botão direito no aplicativo do ser terminal e vá na opção "Obter Informações"
  3. Na janela que se abrir, ative a opção "Abrir com Rosetta"

Finder

Após isso abra o terminal, porque agora precisamos instalar o NodeJS dentro do terminal iniciado com o Rosetta. Eu sou adepto do NVM (Node Version Manager), facilita bastante a vida pra trocar a versão do Node rapidamente, então eu segui assim:

  1. Rodei o comando nvm install 14 --shared-zlib para instalar o Node.

Instalei a versão 14 pois é a mais atual que o plugin serverless-esbuild tem suporte, e a parte --shared-zlib é uma configuração que o próprio NVM indica para evitar erros ao instalar o Node em outra arquitetura dentro do M1.

  1. Rodei node -v para conferir a versão
  2. E rodei node -p process.arch para conferir a arquitetura que o Node foi instalado O retorno deve ser i386 ou x64

Finalizada a configuração do terminal e do Node, podemos partir pro projeto! Pra isso podemos fazer o seguinte, caso ainda não tenha criado um projeto:

  1. serverless create --template aws-nodejs-typescript --path serverless para criar o projeto
  2. yarn para instalar as dependências
  3. yarn add serverless-offline -D para instalar o plugin que permite rodar a aplicação localmente como dependência de desenvolvimento
  4. Adicione o plugin na lista de plugins do arquivo serverless.ts como na imagem abaixo:

serverless.ts

  1. Rode o comando serverless offline

Se o retorno for mais ou menos como na imagem abaixo, está tudo funcionando! :confetti_ball:

Terminal


Por hoje é só pessoal!! Espero que seja de ajuda o que escrevi por aqui!! Qualquer dúvida, sugestões e feedbacks deixem nos comentários ou me chamem no LinkedIn também.

Até a próxima!!