Trabalhando com suporte a TypeScript e Jest: um tutorial do AWS SAM
Publicados: 2022-03-11Uma ferramenta poderosa para criar aplicativos sem servidor, o AWS Serverless Application Model (SAM) frequentemente combina com JavaScript: 62% dos desenvolvedores em empresas de médio e grande porte escolhem JavaScript para seu código sem servidor. No entanto, o TypeScript está crescendo em popularidade e supera em muito o JavaScript como a terceira linguagem mais amada pelos desenvolvedores.
Embora o clichê do JavaScript não seja difícil de encontrar, iniciar projetos do AWS SAM com o TypeScript é mais complexo. O tutorial a seguir mostra como criar um projeto do AWS SAM TypeScript do zero e como as diferentes partes funcionam juntas. Os leitores precisam estar apenas um pouco familiarizados com as funções do AWS Lambda para acompanhar.
Iniciando nosso projeto do AWS SAM TypeScript
A base do nosso aplicativo sem servidor inclui vários componentes. Primeiro configuraremos o ambiente da AWS, nosso pacote npm e a funcionalidade do Webpack – então podemos criar, invocar e testar nossa função Lambda para ver nosso aplicativo em ação.
Prepare o Ambiente
Para configurar o ambiente AWS, precisamos instalar o seguinte:
- AWS CLI
- CLI do AWS SAM
- Node.js e npm
Observe que este tutorial requer a instalação do Docker durante a etapa 2 acima para testar nosso aplicativo localmente.
Inicializar um projeto vazio
Vamos criar o diretório do projeto, aws-sam-typescript-boilerplate e uma subpasta src para armazenar o código. No diretório do projeto, vamos configurar um novo pacote npm:
npm init -y # -y option skips over project questionnaire Este comando criará um arquivo package.json dentro do nosso projeto.
Adicione a configuração do Webpack
Webpack é um empacotador de módulos usado principalmente para aplicativos JavaScript. Como o TypeScript compila para JavaScript simples, o Webpack preparará efetivamente nosso código para o navegador da web. Vamos instalar duas bibliotecas e um carregador personalizado:
- webpack: biblioteca principal
- webpack-cli: utilitários de linha de comando para Webpack
- ts-loader: carregador TypeScript para Webpack
npm i --save-dev webpack webpack-cli ts-loader O comando build da AWS SAM CLI, sam build , retarda o processo de desenvolvimento porque tenta executar npm install para cada função, causando duplicação. Usaremos um comando de compilação alternativo da biblioteca aws-sam-webpack-plugin para acelerar nosso ambiente.
npm i --save-dev aws-sam-webpack-plugin Por padrão, o Webpack não fornece um arquivo de configuração. Vamos criar um arquivo de configuração personalizado chamado webpack.config.js na pasta raiz:
/* eslint-disable @typescript-eslint/no-var-requires */ const path = require('path'); const AwsSamPlugin = require('aws-sam-webpack-plugin'); const awsSamPlugin = new AwsSamPlugin(); module.exports = { entry: () => awsSamPlugin.entry(), output: { filename: (chunkData) => awsSamPlugin.filename(chunkData), libraryTarget: 'commonjs2', path: path.resolve('.') }, devtool: 'source-map', resolve: { extensions: ['.ts', '.js'] }, target: 'node', mode: process.env.NODE_ENV || 'development', module: { rules: [{ test: /\.tsx?$/, loader: 'ts-loader' }] }, plugins: [awsSamPlugin] };Agora vamos examinar as várias partes:
-
entry: carrega o objeto entry (onde o Webpack começa a construir o pacote) do recursoAWS::Serverless::Function. -
output: aponta para o destino da saída da compilação (neste caso,.aws-sam/build). Aqui também especificamos a biblioteca de destino comocommonjs2, que atribui o valor de retorno do ponto de entrada amodule.exports. Esse ponto de entrada é o padrão para ambientes Node.js. -
devtool: isso cria um mapa de origem,app.js.map, em nosso destino de saída de compilação. Ele mapeia nosso código original para o código em execução no navegador da Web e ajudará na depuração se definirmos a variável de ambienteNODE_OPTIONScomo--enable-source-mapspara nosso Lambda. -
resolve: Isso diz ao Webpack para processar arquivos TypeScript antes dos arquivos JavaScript. -
target: Isso diz ao Webpack para direcionar o Node.js como nosso ambiente. Isso significa que o Webpack usará a funçãorequiredo Node.js para carregar partes quando compilar. -
module: aplica o carregador TypeScript a todos os arquivos que atendem à condição detest. Em outras palavras, garante que todos os arquivos com extensão.tsou.tsxserão manipulados pelo carregador. -
plugins: Isso ajuda o Webpack a identificar e usar nossoaws-sam-webpack-plugin.
Na primeira linha, desabilitamos uma regra ESLint específica para este arquivo. As regras ESLint padrão que configuraremos posteriormente desencorajam o uso da instrução require . Preferimos require a import no Webpack, então abriremos uma exceção.
Configurar o suporte ao TypeScript
Adicionar suporte ao TypeScript melhorará a experiência do desenvolvedor ao:
- Evitando mensagens de aviso sobre declarações de tipo ausentes.
- Fornecendo validação de tipo.
- Oferecendo autocompletar dentro do IDE.
Primeiro, instalaremos o TypeScript para nosso projeto localmente (pule esta etapa se você tiver o TypeScript instalado globalmente):
npm i --save-dev typescriptIncluiremos os tipos para as bibliotecas que estamos usando:
npm i --save-dev @types/node @types/webpack @types/aws-lambda Agora, criaremos o arquivo de configuração do TypeScript, tsconfig.json , na raiz do projeto:
{ "compilerOptions": { "target": "ES2015", "module": "commonjs", "sourceMap": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, }, "include": ["src/**/*.ts", "src/**/*.js"], "exclude": ["node_modules"] } Aqui estamos seguindo a configuração padrão recomendada pela comunidade TypeScript. Adicionamos include para anexar os arquivos na pasta src ao programa e exclude para evitar a compilação TypeScript para a pasta node_modules —não tocaremos nesse código diretamente.
Criar uma função Lambda
Não escrevemos nenhum código Lambda para nosso aplicativo sem servidor até agora, então vamos começar. Na pasta src que criamos anteriormente, criaremos uma subpasta test-lambda contendo um arquivo app.ts com esta função Lambda:
import { APIGatewayEvent } from 'aws-lambda'; export const handler = async (event: APIGatewayEvent) => { console.log('incoming event is', JSON.stringify(event)); const response = { statusCode: 200, body: JSON.stringify({ message: 'Request was successful.' }) }; return response; };Essa função de espaço reservado simples retorna uma resposta 200 com um corpo. Seremos capazes de executar o código após mais uma etapa.
Incluir o arquivo de modelo da AWS
O AWS SAM requer um arquivo de modelo para transpilar nosso código e implantá-lo na nuvem. Crie o arquivo template.yaml na pasta raiz:
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: AWS SAM Boilerplate Using TypeScript Globals: Function: Runtime: nodejs14.x # modify the version according to your need Timeout: 30 Resources: TestLambda: Type: AWS::Serverless::Function Properties: Handler: app.handler FunctionName: "Test-Lambda" CodeUri: src/test-lambda/ Events: ApiEvent: Type: Api Properties: Path: /test Method: get Esse arquivo de modelo gera uma função Lambda acessível a partir de uma API HTTP GET. Observe que a versão referenciada na linha Runtime: pode precisar de personalização.
Execute o aplicativo
Para executar a aplicação, devemos adicionar um novo script no arquivo package.json para construir o projeto com o Webpack. O arquivo pode ter scripts existentes, como um script de teste vazio. Podemos adicionar o script de construção assim:
"scripts": { "build": "webpack-cli" } Se você executar npm run build na raiz do projeto, deverá ver a pasta build, .aws-sam , criada. Aqueles de nós em um ambiente Mac podem precisar tornar os arquivos ocultos visíveis pressionando Command + Shift + . para ver a pasta.
Vamos agora iniciar um servidor HTTP local para testar nossa função:
sam local start-apiQuando visitamos o endpoint de teste em um navegador da Web, devemos ver uma mensagem de sucesso.
O console deve mostrar que a função é montada em um contêiner do Docker antes de ser executada, e é por isso que instalamos o Docker anteriormente:
Invoking app.handler (nodejs14.x) Skip pulling image and use local one: public.ecr.aws/sam/emulation-nodejs14.x:rapid-1.37.0-x86_64. Mounting /Users/mohammadfaisal/Documents/learning/aws-sam-typescript-boilerplate/.aws-sam/build/TestLambda as /var/task:ro, delegated inside runtime containerAprimorando nosso fluxo de trabalho de desenvolvimento para um ambiente profissional
Nosso projeto está funcionando, adicionar alguns toques finais garantirá uma experiência de desenvolvedor excepcional que aumentará a produtividade e a colaboração.

Otimize a compilação com o Hot Reloading
É tedioso executar o comando build após cada alteração de código. O recarregamento a quente resolverá esse problema. Podemos adicionar outro script em nosso package.json para observar as alterações no arquivo:
"watch": "webpack-cli -w" Abra um terminal separado e execute npm run watch . Agora, seu projeto será compilado automaticamente quando você alterar qualquer código. Modifique a mensagem do código, atualize sua página da Web e veja o resultado atualizado.
Melhore a qualidade do código com ESLint e Prettier
Nenhum projeto TypeScript ou JavaScript está completo sem ESLint e Prettier. Essas ferramentas manterão a qualidade e a consistência do código do seu projeto.
Vamos instalar as dependências principais primeiro:
npm i --save-dev eslint prettierAdicionaremos algumas dependências auxiliares para que o ESLint e o Prettier possam trabalhar juntos em nosso projeto TypeScript:
npm i --save-dev \ eslint-config-prettier \ eslint-plugin-prettier \ @typescript-eslint/parser \ @typescript-eslint/eslint-plugin Em seguida, adicionaremos nosso linter criando um arquivo de configuração ESLint, .eslintrc , dentro da raiz do projeto:
{ "root": true, "env": { "es2020": true, "node": true, "jest": true }, "parser": "@typescript-eslint/parser", "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" ], "ignorePatterns": ["src/**/*.test.ts", "dist/", "coverage/", "test/"], "parserOptions": { "ecmaVersion": 2018, "sourceType": "module", "ecmaFeatures": { "impliedStrict": true } }, "rules": { "quotes": ["error", "single", { "allowTemplateLiterals": true }], "default-case": "warn", "no-param-reassign": "warn", "no-await-in-loop": "warn", "@typescript-eslint/no-unused-vars": [ "error", { "vars": "all", "args": "none" } ] }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".jsx", ".ts", ".tsx"] } } } } Observe que a seção extends do nosso arquivo deve manter a configuração do plugin Prettier como a última linha para exibir erros Prettier como erros ESLint visíveis em nosso editor. Estamos seguindo as configurações recomendadas do ESLint para TypeScript, com algumas preferências personalizadas adicionadas na seção de rules . Sinta-se à vontade para navegar pelas regras disponíveis e personalizar ainda mais suas configurações. Optamos por incluir:
- Um erro se não usarmos strings entre aspas simples.
- Um aviso quando não fornecemos maiúsculas e minúsculas
defaultnas instruçõesswitch. - Um aviso se reatribuirmos qualquer parâmetro de uma função.
- Um aviso se chamarmos uma instrução
awaitdentro de um loop. - Um erro para variáveis não utilizadas, que tornam o código ilegível e propenso a erros ao longo do tempo.
Já configuramos nossa configuração ESLint para trabalhar com a formatação Prettier. (Mais informações estão disponíveis no projeto GitHub eslint-config-prettier .) Agora, podemos criar o arquivo de configuração do Prettier, .prettierrc :
{ "trailingComma": "none", "tabWidth": 4, "semi": true, "singleQuote": true }Essas configurações são da documentação oficial da Prettier; você pode modificá-los como desejar. Atualizamos as seguintes propriedades:
-
trailingComma: Alteramos isso dees5paranonepara evitar vírgulas à direita. -
semi: Mudamos isso defalseparatrueporque preferimos ter um ponto e vírgula no final de cada linha.
Finalmente, é hora de ver o ESLint e o Prettier em ação. Em nosso arquivo app.ts , alteraremos o tipo de variável de response de const para let . Usar let não é uma boa prática neste caso, pois não modificamos o valor de response . O editor deve exibir um erro, a regra quebrada e sugestões para corrigir o código. Não se esqueça de habilitar o ESLint e o Prettier em seu editor se eles ainda não estiverem configurados.
Manter código com teste Jest
Muitas bibliotecas estão disponíveis para teste, como Jest, Mocha e Storybook. Usaremos o Jest em nosso projeto por alguns motivos:
- É rápido para aprender.
- Requer configuração mínima.
- Ele oferece testes de instantâneos fáceis de usar.
Vamos instalar as dependências necessárias:
npm i --save-dev jest ts-jest @types/jest Em seguida, criaremos um arquivo de configuração Jest, jest.config.js , dentro da raiz do projeto:
module.exports = { roots: ['src'], testMatch: ['**/__tests__/**/*.+(ts|tsx|js)'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest' } };Estamos personalizando três opções em nosso arquivo:
-
roots: Este array contém as pastas que serão pesquisadas por arquivos de teste—ele verifica apenas abaixo de nossa subpastasrc. -
testMatch: Esta matriz de padrões glob inclui as extensões de arquivo que serão consideradas arquivos Jest. -
transform: Esta opção nos permite escrever nossos testes em TypeScript usando o pacotets-jest.
Vamos criar uma nova pasta __tests__ dentro src/test-lambda . Dentro dele, adicionaremos o arquivo handler.test.ts , onde criaremos nosso primeiro teste:
import { handler } from '../app'; const event: any = { body: JSON.stringify({}), headers: {} }; describe('Demo test', () => { test('This is the proof of concept that the test works.', async () => { const res = await handler(event); expect(res.statusCode).toBe(200); }); }); Retornaremos ao nosso arquivo package.json e o atualizaremos com o script de teste:
"test": "jest" Quando vamos ao terminal e executamos npm run test , devemos ser recebidos com um teste aprovado:
Manipular o controle de origem com .gitignore
Devemos configurar o Git para excluir determinados arquivos do controle de origem. Podemos criar um arquivo .gitignore usando gitignore.io para pular arquivos que não são necessários:
# Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Runtime data pids *.pid *.seed *.pid.lock npm-debug.log package.lock.json /node_modules .aws-sam .vscode # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional ESLint cache .eslintcachePreparar, definir, construir: nosso plano para o sucesso
Agora temos um projeto clichê completo do AWS SAM com TypeScript. Nós nos concentramos em acertar o básico e manter a alta qualidade do código com suporte a ESLint, Prettier e Jest. O exemplo deste tutorial do AWS SAM pode servir como um modelo, colocando seu próximo grande projeto nos trilhos desde o início.
O Toptal Engineering Blog agradece a Christian Loef por revisar os exemplos de código apresentados neste artigo.
