使用 TypeScript 和 Jest 支持:AWS SAM 教程

已發表: 2022-03-11

作為構建無服務器應用程序的強大工具,AWS 無服務器應用程序模型 (SAM) 經常與 JavaScript 搭配使用:大中型公司中有 62% 的開發人員選擇 JavaScript 作為其無服務器代碼。 然而,TypeScript 的受歡迎程度飆升,遠遠超過 JavaScript,成為開發人員第三大最受歡迎的語言。

雖然 JavaScript 樣板文件不難找到,但使用 TypeScript 啟動 AWS SAM 項目更為複雜。 以下教程展示瞭如何從頭開始創建 AWS SAM TypeScript 項目,以及不同部分如何協同工作。 讀者只需對 AWS Lambda 函數稍有熟悉即可了解。

啟動我們的 AWS SAM TypeScript 項目

我們的無服務器應用程序的基礎包括各種組件。 我們將首先配置 AWS 環境、我們的 npm 包和 Webpack 功能——然後我們可以創建、調用和測試我們的 Lambda 函數以查看我們的應用程序的運行情況。

準備環境

要設置 AWS 環境,我們需要安裝以下內容:

  1. AWS CLI
  2. AWS SAM CLI
  3. Node.js 和 npm

請注意,本教程需要在上面的第 2 步中安裝 Docker 以在本地測試我們的應用程序。

初始化一個空項目

讓我們創建項目目錄aws-sam-typescript-boilerplate和一個src子文件夾來保存代碼。 從項目目錄中,我們將設置一個新的 npm 包:

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

該命令將在我們的項目中創建一個package.json文件。

添加 Webpack 配置

Webpack 是一個模塊打包器,主要用於 JavaScript 應用程序。 由於 TypeScript 編譯為純 JavaScript,Webpack 將有效地為 Web 瀏覽器準備我們的代碼。 我們將安裝兩個庫和一個自定義加載器:

  • webpack:核心庫
  • webpack-cli:Webpack 的命令行實用程序
  • ts-loader:用於 Webpack 的 TypeScript 加載器
npm i --save-dev webpack webpack-cli ts-loader

AWS SAM CLI 構建命令sam build會減慢開發過程,因為它會嘗試為每個函數運行npm install ,從而導致重複。 我們將使用 aws-sam-webpack-plugin 庫中的替代構建命令來加速我們的環境。

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

默認情況下,Webpack 不提供配置文件。 讓我們在根文件夾中創建一個名為webpack.config.js的自定義配置文件:

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

現在讓我們檢查各個部分:

  • entry :這會從AWS::Serverless::Function資源加載 entry 對象(Webpack 開始構建包的地方)。
  • output :這指向構建輸出的目的地(在本例中為.aws-sam/build )。 這裡我們還將目標庫指定為commonjs2 ,它將入口點的返回值分配給module.exports 。 此入口點是 Node.js 環境的默認入口點。
  • devtool :這會在我們的構建輸出目標中創建一個源映射app.js.map 。 它將我們的原始代碼映射到在 Web 瀏覽器中運行的代碼,如果我們將環境變量NODE_OPTIONS設置為 Lambda 的--enable-source-maps ,它將有助於調試。
  • resolve :這告訴 Webpack 在 JavaScript 文件之前處理 TypeScript 文件。
  • target :這告訴 Webpack 將 Node.js 作為我們的環境。 這意味著 Webpack 在編譯時將使用 Node.js 的require函數來加載塊。
  • module :這會將 TypeScript 加載器應用於所有滿足test條件的文件。 換句話說,它確保所有具有.ts.tsx擴展名的文件都將由加載程序處理。
  • plugins :這有助於 Webpack 識別和使用我們的aws-sam-webpack-plugin

在第一行,我們為這個文件禁用了一個特定的 ESLint 規則。 我們稍後將配置的標準 ESLint 規則不鼓勵使用require語句。 我們更喜歡在 Webpack 中使用require import ,所以我們會例外。

設置 TypeScript 支持

添加 TypeScript 支持將通過以下方式改善開發人員體驗:

  • 防止有關缺少類型聲明的警告消息。
  • 提供類型驗證。
  • 在 IDE 中提供自動完成功能。

首先,我們將在本地為我們的項目安裝 TypeScript(如果您已全局安裝了 TypeScript,請跳過此步驟):

 npm i --save-dev typescript

我們將包括我們正在使用的庫的類型:

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

現在,我們將在項目根目錄中創建 TypeScript 配置文件tsconfig.json

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

這裡我們遵循 TypeScript 社區推薦的默認配置。 我們添加了include以將src文件夾下的文件附加到程序中,並添加exclude以避免對node_modules文件夾進行 TypeScript 編譯——我們不會直接接觸此代碼。

創建 Lambda 函數

到目前為止,我們還沒有為我們的無服務器應用程序編寫任何 Lambda 代碼,所以讓我們開始吧。在我們之前創建的src文件夾中,我們將創建一個test-lambda子文件夾,其中包含一個帶有此 Lambda 函數的app.ts文件:

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

這個簡單的佔位符函數返回一個帶有正文的 200 響應。 再經過一步,我們將能夠運行代碼。

包括 AWS 模板文件

AWS SAM 需要一個模板文件來轉譯我們的代碼並將其部署到雲中。 在根文件夾中創建文件template.yaml

 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

此模板文件生成可從 HTTP GET API 訪問的 Lambda 函數。 請注意,在Runtime:行中引用的版本可能需要自定義。

運行應用程序

要運行應用程序,我們必須在package.json文件中添加一個新腳本,以便使用 Webpack 構建項目。 該文件可能有現有的腳本,例如一個空的測試腳本。 我們可以像這樣添加構建腳本:

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

如果您從項目的根目錄運行npm run build ,您應該會看到構建文件夾.aws-sam創建。 我們這些在 Mac 環境中的人可能需要通過按Command + Shift + 使隱藏文件可見。 查看文件夾。

我們現在將啟動一個本地 HTTP 服務器來測試我們的功能:

 sam local start-api

當我們在 Web 瀏覽器中訪問測試端點時,我們應該會看到一條成功消息。

Web 瀏覽器在地址欄中顯示鏈接“127.0.0.1:3000/test”。地址欄下方的網頁是空白的,除了一條消息“{“message”:“請求成功。”}。

控制台應該顯示該函數在運行之前已安裝在 Docker 容器中,這就是我們之前安裝 Docker 的原因:

 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

為專業環境增強我們的開發工作流程

我們的項目已啟動並正在運行,添加一些收尾工作將確保卓越的開發人員體驗,從而提高生產力和協作。

使用熱重載優化構建

每次代碼更改後運行構建命令很乏味。 熱重載將解決此問題。 我們可以在package.json中添加另一個腳本來監視文件更改:

 "watch": "webpack-cli -w"

打開一個單獨的終端並運行npm run watch 。 現在,當您更改任何代碼時,您的項目將自動編譯。 修改代碼的消息,刷新你的網頁,看看更新的結果。

使用 ESLint 和 Prettier 提高代碼質量

沒有 ESLint 和 Prettier,任何 TypeScript 或 JavaScript 項目都是不完整的。 這些工具將保持您項目的代碼質量和一致性。

讓我們先安裝核心依賴項:

 npm i --save-dev eslint prettier

我們將添加一些輔助依賴項,以便 ESLint 和 Prettier 可以在我們的 TypeScript 項目中協同工作:

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

接下來,我們將通過在項目根目錄中創建一個 ESLint 配置文件.eslintrc來添加我們的 linter:

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

請注意,我們文件的extends部分必須將 Prettier 插件配置保留在最後一行,以便將 Prettier 錯誤顯示為我們編輯器中可見的 ESLint 錯誤。 我們遵循 ESLint 為 TypeScript 推薦的設置,並在rules部分添加了一些自定義首選項。 隨意瀏覽可用規則並進一步自定義您的設置。 我們選擇包括:

  • 如果我們不使用單引號字符串,則會出錯。
  • 當我們在switch語句中不提供default情況時發出警告。
  • 如果我們重新分配函數的任何參數,則會發出警告。
  • 如果我們在循環中調用await語句,則會發出警告。
  • 未使用變量的錯誤,這會使代碼隨著時間的推移變得不可讀且容易出錯。

我們已經設置了 ESLint 配置以使用 Prettier 格式。 (更多信息可以在eslint-config-prettier GitHub 項目中找到。)現在,我們可以創建 Prettier 配置文件.prettierrc

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

這些設置來自 Prettier 的官方文檔; 您可以根據需要修改它們。 我們更新了以下屬性:

  • trailingComma :我們將其從es5更改為none以避免尾隨逗號。
  • semi :我們將其從false更改為true ,因為我們更喜歡在每行的末尾有一個分號。

最後,是時候看看 ESLint 和 Prettier 的實際應用了。 在我們的app.ts文件中,我們將response變量類型從const更改為let 。 在這種情況下使用let不是一個好習慣,因為我們不修改response的值。 編輯器應顯示錯誤、損壞的規則和修復代碼的建議。 如果尚未設置,請不要忘記在您的編輯器上啟用 ESLint 和 Prettier。

編輯器顯示一行代碼,為變量“let response”賦值。該行旁邊顯示一個黃色燈泡,“響應”一詞有一個紅色下劃線和一個錯誤彈出窗口。錯誤彈窗首先定義了變量“response”,內容為:“let response: { statusCode: number; body: string; }。”在定義下方,錯誤消息顯示:“'response' 永遠不會重新分配。請改用 'const'。eslint(prefer-const)。”在錯誤消息下方,有兩個選項顯示:“查看問題”或“快速修復”。

使用 Jest 測試維護代碼

許多庫可用於測試,例如 Jest、Mocha 和 Storybook。 我們將在我們的項目中使用 Jest 有幾個原因:

  • 學起來很快。
  • 它需要最少的設置。
  • 它提供易於使用的快照測試。

讓我們安裝所需的依賴項:

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

接下來,我們將在項目根目錄中創建一個 Jest 配置文件jest.config.js

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

我們在我們的文件中自定義了三個選項:

  • roots :這個數組包含將要搜索測試文件的文件夾——它只檢查我們的src子文件夾下。
  • testMatch :這個 glob 模式數組包括將被視為 Jest 文件的文件擴展名。
  • transform :這個選項讓我們可以使用ts-jest包在 TypeScript 中編寫測試。

讓我們在src/test-lambda中創建一個新的__tests__文件夾。 在其中,我們將添加文件handler.test.ts ,我們將在其中創建我們的第一個測試:

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

我們將返回到我們的package.json文件並使用測試腳本對其進行更新:

 "test": "jest"

當我們去終端並運行npm run test時,我們應該會看到一個通過測試:

控制台頂部顯示一個綠色的“通過”指示器和測試文件名“src/test-lambda/__tests__/handler.test.ts”。下一行是“演示測試”。下一行顯示一個綠色複選標記,後跟“這是測試有效的概念證明。(1 毫秒)”。在空行之後,第一行顯示:“測試套件:1 個通過,總共 1 個。”第二個內容是:“測試:1 次通過,總共 1 次。”第三個內容是:“快照:總共 0 個。”第四個寫著:“時間:0.959 秒。”最後一行顯示:“運行所有測試套件。”

使用.gitignore處理源代碼管理

我們應該配置 Git 從源代碼控制中排除某些文件。 我們可以使用 gitignore.io 創建一個.gitignore文件來跳過不需要的文件:

 # 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

準備、設置、構建:我們的成功藍圖

我們現在有了一個完整的帶有 TypeScript 的 AWS SAM 樣板項目。 我們專注於通過 ESLint、Prettier 和 Jest 支持獲得正確的基礎知識並保持高代碼質量。 此 AWS SAM 教程中的示例可以作為藍圖,讓您的下一個大項目從一開始就走上正軌。

Toptal 工程博客對 Christian Loef 對本文中提供的代碼示例的審閱表示感謝。

帶有“PARTNER”字樣的 AWS 徽標和下方的“Advanced Tier Services”文本。
作為亞馬遜合作夥伴網絡 (APN) 中的高級諮詢合作夥伴,Toptal 為公司提供在世界任何地方按需訪問 AWS 認證專家的機會。