Lucrul cu TypeScript și suport Jest: un tutorial AWS SAM

Publicat: 2022-03-11

Un instrument puternic pentru construirea de aplicații fără server, modelul de aplicații fără server (SAM) AWS se asociază frecvent cu JavaScript: 62% dintre dezvoltatorii companiilor mijlocii și mari aleg JavaScript pentru codul lor fără server. Cu toate acestea, TypeScript este în creștere în popularitate și depășește cu mult JavaScript ca al treilea cel mai iubit limbaj al dezvoltatorilor.

Deși standardul JavaScript nu este greu de găsit, pornirea proiectelor AWS SAM cu TypeScript este mai complexă. Următorul tutorial arată cum să creați un proiect AWS SAM TypeScript de la zero, precum și modul în care diferitele părți funcționează împreună. Cititorii trebuie să fie doar oarecum familiarizați cu funcțiile AWS Lambda pentru a urma.

Începem proiectul nostru AWS SAM TypeScript

Fundamentul aplicației noastre fără server include diverse componente. Mai întâi vom configura mediul AWS, pachetul nostru npm și funcționalitatea Webpack – apoi putem crea, invoca și testa funcția noastră Lambda pentru a vedea aplicația noastră în acțiune.

Pregătiți Mediul

Pentru a configura mediul AWS, trebuie să instalăm următoarele:

  1. AWS CLI
  2. AWS SAM CLI
  3. Node.js și npm

Rețineți că acest tutorial necesită instalarea Docker în timpul pasului 2 de mai sus pentru a testa aplicația noastră local.

Inițializați un proiect gol

Să creăm directorul proiectului, aws-sam-typescript-boilerplate și un subfolder src pentru a păstra codul. Din directorul de proiect, vom configura un nou pachet npm:

 npm init -y # -y option skips over project questionnaire

Această comandă va crea un fișier package.json în interiorul proiectului nostru.

Adăugați configurația Webpack

Webpack este un bundler de module utilizat în principal pentru aplicațiile JavaScript. Deoarece TypeScript se compilează în JavaScript simplu, Webpack va pregăti efectiv codul nostru pentru browserul web. Vom instala două biblioteci și un încărcător personalizat:

  • webpack: bibliotecă de bază
  • webpack-cli: utilitare de linie de comandă pentru Webpack
  • ts-loader: încărcător TypeScript pentru Webpack
 npm i --save-dev webpack webpack-cli ts-loader

Comanda de compilare AWS SAM CLI, sam build , încetinește procesul de dezvoltare deoarece încearcă să ruleze npm install pentru fiecare funcție, provocând dublarea. Vom folosi o comandă de compilare alternativă din biblioteca aws-sam-webpack-plugin pentru a accelera mediul nostru.

 npm i --save-dev aws-sam-webpack-plugin

În mod implicit, Webpack nu oferă un fișier de configurare. Să facem un fișier de configurare personalizat numit webpack.config.js în folderul rădăcină:

 /* 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] };

Acum să examinăm diferitele părți:

  • entry : Aceasta încarcă obiectul de intrare (unde Webpack începe să construiască pachetul) din resursa AWS::Serverless::Function .
  • output : Aceasta indică destinația ieșirii de compilare (în acest caz, .aws-sam/build ). Aici specificăm și biblioteca țintă ca commonjs2 , care atribuie valoarea de returnare a punctului de intrare module.exports . Acest punct de intrare este implicit pentru mediile Node.js.
  • devtool : Aceasta creează o hartă sursă, app.js.map , în destinația noastră de ieșire a compilației. Mapează codul nostru original la codul care rulează în browserul web și va ajuta la depanare dacă setăm variabila de mediu NODE_OPTIONS la --enable-source-maps pentru Lambda noastră.
  • resolve : Aceasta îi spune lui Webpack să proceseze fișierele TypeScript înaintea fișierelor JavaScript.
  • target : Acest lucru îi spune lui Webpack să vizeze Node.js ca mediu. Aceasta înseamnă că Webpack va folosi funcția Node.js require pentru a încărca bucăți atunci când se compilează.
  • module : Acesta aplică încărcătorul TypeScript la toate fișierele care îndeplinesc condiția de test . Cu alte cuvinte, se asigură că toate fișierele cu .ts sau .tsx vor fi gestionate de încărcător.
  • plugins : aceasta ajută Webpack să identifice și să utilizeze pluginul nostru aws-sam-webpack-plugin .

În prima linie, am dezactivat o anumită regulă ESLint pentru acest fișier. Regulile standard ESLint pe care le vom configura ulterior descurajează utilizarea instrucțiunii require . Preferăm să import în require , așa că vom face o excepție.

Configurați suport TypeScript

Adăugarea suportului TypeScript va îmbunătăți experiența dezvoltatorului prin:

  • Prevenirea mesajelor de avertizare cu privire la lipsa declarațiilor de tip.
  • Furnizarea de validare a tipului.
  • Oferind completare automată în interiorul IDE.

Mai întâi, vom instala local TypeScript pentru proiectul nostru (săriți peste acest pas dacă aveți TypeScript instalat la nivel global):

 npm i --save-dev typescript

Vom include tipurile pentru bibliotecile pe care le folosim:

 npm i --save-dev @types/node @types/webpack @types/aws-lambda

Acum, vom crea fișierul de configurare TypeScript, tsconfig.json , în rădăcina proiectului:

 { "compilerOptions": { "target": "ES2015", "module": "commonjs", "sourceMap": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, }, "include": ["src/**/*.ts", "src/**/*.js"], "exclude": ["node_modules"] }

Aici urmărim configurația implicită recomandată de comunitatea TypeScript. Am adăugat include pentru a adăuga fișierele din folderul src la program și am exclude pentru a evita compilarea TypeScript pentru folderul node_modules - nu vom atinge acest cod direct.

Creați o funcție Lambda

Nu am scris niciun cod Lambda pentru aplicația noastră fără server până acum, așa că haideți să intrăm. În folderul src creat mai devreme, vom crea un subfolder test-lambda care conține un fișier app.ts cu această funcție 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; };

Această funcție simplă de substituent returnează un răspuns 200 cu un corp. Vom putea rula codul după încă un pas.

Includeți fișierul șablon AWS

AWS SAM necesită un fișier șablon pentru a transpila codul nostru și a-l implementa în cloud. Creați fișierul template.yaml în folderul rădăcină:

 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

Acest fișier șablon generează o funcție Lambda accesibilă dintr-un API GET HTTP. Rețineți că versiunea la care se face referire în linia Runtime: poate necesita personalizare.

Rulați aplicația

Pentru a rula aplicația, trebuie să adăugăm un nou script în fișierul package.json pentru construirea proiectului cu Webpack. Fișierul poate avea scripturi existente, cum ar fi un script de testare gol. Putem adăuga scriptul de compilare astfel:

 "scripts": { "build": "webpack-cli" }

Dacă rulați npm run build de la rădăcina proiectului, ar trebui să vedeți folderul de compilare, .aws-sam , creat. Aceia dintre noi într-un mediu Mac poate avea nevoie să facă vizibile fișierele ascunse apăsând Command + Shift + . pentru a vedea folderul.

Acum vom porni un server HTTP local pentru a ne testa funcția:

 sam local start-api

Când vizităm punctul final de testare într-un browser web, ar trebui să vedem un mesaj de succes.

Browserul web afișează linkul „127.0.0.1:3000/test” în bara de adrese. Sub bara de adrese, pagina web este goală, cu excepția unui mesaj care scrie „{"message": "Solicitarea a avut succes."}.

Consola ar trebui să arate că funcția este montată într-un container Docker înainte de a rula, motiv pentru care am instalat Docker mai devreme:

 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 container

Îmbunătățirea fluxului nostru de lucru de dezvoltare pentru un cadru profesional

Proiectul nostru este în funcțiune, adăugarea de câteva retușuri finale va asigura o experiență de dezvoltator excepțională, care va crește productivitatea și colaborarea.

Optimizați construcția cu reîncărcarea la cald

Este plictisitor să rulezi comanda build după fiecare schimbare de cod. Reîncărcarea la cald va rezolva această problemă. Putem adăuga un alt script în package.json pentru a urmări modificările fișierelor:

 "watch": "webpack-cli -w"

Deschideți un terminal separat și rulați npm run watch . Acum, proiectul dvs. se va compila automat când modificați orice cod. Modificați mesajul codului, reîmprospătați pagina web și vedeți rezultatul actualizat.

Îmbunătățiți calitatea codului cu ESLint și Prettier

Niciun proiect TypeScript sau JavaScript nu este complet fără ESLint și Prettier. Aceste instrumente vor menține calitatea și consistența codului proiectului dvs.

Să instalăm mai întâi dependențele de bază:

 npm i --save-dev eslint prettier

Vom adăuga câteva dependențe de ajutor, astfel încât ESLint și Prettier să poată lucra împreună în proiectul nostru TypeScript:

 npm i --save-dev \ eslint-config-prettier \ eslint-plugin-prettier \ @typescript-eslint/parser \ @typescript-eslint/eslint-plugin

În continuare, vom adăuga linter-ul nostru creând un fișier de configurare ESLint, .eslintrc , în interiorul rădăcinii proiectului:

 { "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"] } } } }

Rețineți că secțiunea extends a fișierului nostru trebuie să păstreze configurația pluginului Prettier ca ultimă linie pentru a afișa erorile Prettier ca erori ESLint vizibile în editorul nostru. Urmăm setările recomandate de ESLint pentru TypeScript, cu unele preferințe personalizate adăugate în secțiunea de rules . Simțiți-vă liber să răsfoiți regulile disponibile și să personalizați în continuare setările. Am ales să includem:

  • O eroare dacă nu folosim șiruri cu ghilimele simple.
  • Un avertisment când nu furnizăm niciun caz default în instrucțiunile switch .
  • Un avertisment dacă reatribum orice parametru al unei funcții.
  • Un avertisment dacă apelăm o instrucțiune await în interiorul unei bucle.
  • O eroare pentru variabilele neutilizate, care fac codul imposibil de citit și predispus la erori în timp.

Am configurat deja configurația noastră ESLint pentru a funcționa cu formatarea Prettier. (Sunt disponibile mai multe informații în proiectul GitHub eslint-config-prettier .) Acum, putem crea fișierul de configurare Prettier, .prettierrc :

 { "trailingComma": "none", "tabWidth": 4, "semi": true, "singleQuote": true }

Aceste setări sunt din documentația oficială a lui Prettier; le puteți modifica după cum doriți. Am actualizat următoarele proprietăți:

  • trailingComma : Am schimbat acest lucru de la es5 la none pentru a evita virgulele finale.
  • semi : Am schimbat acest lucru de la false la true deoarece preferăm să avem un punct și virgulă la sfârșitul fiecărei linii.

În sfârșit, este timpul să vedeți ESLint și Prettier în acțiune. În fișierul nostru app.ts , vom schimba tipul de variabilă de response din const în let . Utilizarea let nu este o practică bună în acest caz, deoarece nu modificăm valoarea response . Editorul ar trebui să afișeze o eroare, regula încălcată și sugestii pentru a remedia codul. Nu uitați să activați ESLint și Prettier în editorul dvs. dacă nu sunt deja configurate.

Editorul afișează o linie de cod care atribuie o valoare variabilei „lasă răspuns”. Linia arată un bec galben lângă el, iar cuvântul „răspuns” are o subliniere roșie și o fereastră pop-up de eroare deasupra. Fereastra pop-up de eroare definește mai întâi variabila „răspuns” și citește: „lasă răspuns: { statusCode: număr; corp: șir; }." Sub definiție, mesajul de eroare arată: „„răspunsul” nu este niciodată reatribuit. Folosiți „const” în schimb. eslint(prefer-const).” Sub mesajul de eroare, există două opțiuni: „Vizualizare problemă” sau „Remediere rapidă”.

Menține codul cu testul Jest

Multe biblioteci sunt disponibile pentru testare, cum ar fi Jest, Mocha și Storybook. Vom folosi Jest în proiectul nostru din câteva motive:

  • Este rapid de învățat.
  • Necesită o configurare minimă.
  • Oferă testare instantanee ușor de utilizat.

Să instalăm dependențele necesare:

 npm i --save-dev jest ts-jest @types/jest

Apoi, vom crea un fișier de configurare Jest, jest.config.js , în rădăcina proiectului:

 module.exports = { roots: ['src'], testMatch: ['**/__tests__/**/*.+(ts|tsx|js)'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest' } };

Personalizăm trei opțiuni în fișierul nostru:

  • roots : această matrice conține folderele care vor fi căutate pentru fișierele de testare — se verifică doar sub subdosarul nostru src .
  • testMatch : Această serie de modele glob include extensiile de fișiere care vor fi considerate fișiere Jest.
  • transform : Această opțiune ne permite să scriem testele noastre în TypeScript folosind pachetul ts-jest .

Să creăm un nou folder __tests__ în interiorul src/test-lambda . În interiorul acestuia, vom adăuga fișierul handler.test.ts , unde vom crea primul nostru test:

 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); }); });

Vom reveni la fișierul nostru package.json și îl vom actualiza cu scriptul de testare:

 "test": "jest"

Când mergem la terminal și rulăm npm run test , ar trebui să fim întâmpinați cu un test de trecere:

Partea superioară a consolei arată un indicator verde „Succes” și numele fișierului de testare, „src/test-lambda/__tests__/handler.test.ts”. Rândul următor scrie „Test demonstrativ”. Următoarea linie arată o bifă verde urmată de „Aceasta este dovada conceptului că testul funcționează. (1 ms).” După o linie goală, prima linie scrie: „Suite de testare: 1 trecut, 1 total”. Al doilea scrie: „Teste: 1 promovat, 1 total”. Al treilea scrie: „Instantanee: 0 total”. Al patrulea scrie: „Timp: 0,959 s”. Ultima linie spune: „Ran all test suites”.

Gestionați controlul sursei cu .gitignore

Ar trebui să configuram Git pentru a exclude anumite fișiere din controlul sursei. Putem crea un fișier .gitignore folosind gitignore.io pentru a sări peste fișierele care nu sunt necesare:

 # 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 .eslintcache

Pregătiți, pregătiți, construiți: planul nostru pentru succes

Avem acum un proiect complet AWS SAM cu TypeScript. Ne-am concentrat pe obținerea corectă a elementelor de bază și pe menținerea calității înalte a codului cu suportul ESLint, Prettier și Jest. Exemplul din acest tutorial AWS SAM poate servi drept model, punând pe drumul cel bun următorul tău mare proiect încă de la început.

Blogul Toptal Engineering își extinde recunoștința lui Christian Loef pentru revizuirea exemplelor de cod prezentate în acest articol.

Sigla AWS cu cuvântul „PARTENER” și textul „Servicii de nivel avansat” dedesubt.
În calitate de partener de consultanță avansată în Amazon Partner Network (APN), Toptal oferă companiilor acces la experți certificați de AWS, la cerere, oriunde în lume.