การทำงานกับ TypeScript และ Jest Support: บทช่วยสอน AWS SAM
เผยแพร่แล้ว: 2022-03-11เครื่องมืออันทรงพลังสำหรับการสร้างแอปพลิเคชันแบบไร้เซิร์ฟเวอร์ AWS Serverless Application Model (SAM) มักจับคู่กับ JavaScript: 62% ของนักพัฒนาในบริษัทขนาดกลางและขนาดใหญ่เลือกใช้ JavaScript สำหรับโค้ดแบบไร้เซิร์ฟเวอร์ อย่างไรก็ตาม TypeScript กำลังได้รับความนิยมอย่างรวดเร็วและมีอันดับสูงกว่า JavaScript ว่าเป็นภาษาที่นักพัฒนาชื่นชอบมากที่สุดเป็นอันดับสาม
แม้ว่า JavaScript ต้นแบบจะไม่ใช่เรื่องยาก แต่การเริ่มโปรเจ็กต์ AWS SAM ด้วย TypeScript นั้นซับซ้อนกว่า บทช่วยสอนต่อไปนี้แสดงวิธีสร้างโปรเจ็กต์ AWS SAM TypeScript ตั้งแต่เริ่มต้น ตลอดจนวิธีการทำงานร่วมกันของส่วนต่างๆ ผู้อ่านจำเป็นต้องคุ้นเคยกับฟังก์ชัน AWS Lambda เพียงเล็กน้อยเท่านั้นจึงจะปฏิบัติตามได้
การเริ่มต้นโครงการ AWS SAM TypeScript
รากฐานของแอปพลิเคชันแบบไร้เซิร์ฟเวอร์ของเราประกอบด้วยส่วนประกอบต่างๆ ก่อนอื่นเราจะกำหนดค่าสภาพแวดล้อม AWS แพ็คเกจ npm และฟังก์ชัน Webpack จากนั้นเราจะสามารถสร้าง เรียกใช้ และทดสอบฟังก์ชัน Lambda เพื่อดูการทำงานของแอปพลิเคชันของเรา
เตรียมสิ่งแวดล้อม
ในการตั้งค่าสภาพแวดล้อม AWS เราจำเป็นต้องติดตั้งสิ่งต่อไปนี้:
- AWS CLI
- AWS SAM CLI
- Node.js และ npm
โปรดทราบว่าบทช่วยสอนนี้จำเป็นต้องติดตั้ง Docker ระหว่างขั้นตอนที่ 2 ด้านบนเพื่อทดสอบแอปพลิเคชันของเราในเครื่อง
เริ่มต้นโปรเจ็กต์ว่าง
มาสร้างไดเร็กทอรีโปรเจ็กต์ aws-sam-typescript-boilerplate และโฟลเดอร์ย่อย src เพื่อเก็บโค้ด จากไดเรกทอรีโครงการ เราจะตั้งค่าแพ็คเกจ npm ใหม่:
npm init -y # -y option skips over project questionnaire คำสั่งนี้จะสร้างไฟล์ package.json ภายในโครงการของเรา
เพิ่มการกำหนดค่า Webpack
Webpack เป็นโมดูลมัดรวมที่ใช้เป็นหลักสำหรับแอปพลิเคชัน JavaScript เนื่องจาก TypeScript คอมไพล์เป็น JavaScript ธรรมดา Webpack จะเตรียมโค้ดของเราสำหรับเว็บเบราว์เซอร์อย่างมีประสิทธิภาพ เราจะติดตั้งสองไลบรารีและตัวโหลดแบบกำหนดเอง:
- webpack: ไลบรารีหลัก
- webpack-cli: โปรแกรมอรรถประโยชน์บรรทัดคำสั่งสำหรับ Webpack
- ts-loader: ตัวโหลด TypeScript สำหรับ Webpack
npm i --save-dev webpack webpack-cli ts-loader คำสั่งบิลด์ AWS SAM CLI, sam build ทำให้กระบวนการพัฒนาช้าลงเพราะพยายามรันการ npm install สำหรับแต่ละฟังก์ชัน ทำให้เกิดความซ้ำซ้อน เราจะใช้คำสั่ง build ทางเลือกจากไลบรารี 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: สิ่งนี้จะโหลดวัตถุรายการ (โดยที่ Webpack เริ่มสร้างบันเดิล) จากทรัพยากรAWS::Serverless::Function -
output: สิ่งนี้ชี้ไปที่ปลายทางของเอาต์พุตบิลด์ (ในกรณีนี้คือ ..aws-sam/build) ที่นี่ เรายังระบุไลบรารีเป้าหมายเป็นcommonjs2ซึ่งกำหนดค่าส่งคืนของจุดเข้าใช้งานเป็นmodule.exportsจุดเริ่มต้นนี้เป็นค่าเริ่มต้นสำหรับสภาพแวดล้อม Node.js -
devtool: สิ่งนี้จะสร้างแผนที่ต้นทางapp.js.mapในปลายทางเอาต์พุตบิลด์ของเรา มันจับคู่รหัสดั้งเดิมของเรากับรหัสที่ทำงานในเว็บเบราว์เซอร์ และจะช่วยในการดีบักหากเราตั้งค่าตัวแปรสภาพแวดล้อมNODE_OPTIONSเป็น--enable-source-mapsสำหรับแลมบ์ดาของเรา -
resolve: สิ่งนี้บอกให้ Webpack ประมวลผลไฟล์ TypeScript ก่อนไฟล์ JavaScript -
target: สิ่งนี้บอกให้ Webpack กำหนดเป้าหมาย Node.js เป็นสภาพแวดล้อมของเรา ซึ่งหมายความว่า Webpack จะrequireฟังก์ชัน Node.js ในการโหลดชิ้นส่วนเมื่อคอมไพล์ -
module: ใช้ตัวโหลด TypeScript กับไฟล์ทั้งหมดที่ตรงตามเงื่อนไขtestกล่าวอีกนัยหนึ่ง มันช่วยให้แน่ใจว่าไฟล์ทั้งหมดที่มีนามสกุล.tsหรือ ..tsxจะถูกจัดการโดยตัวโหลด -
plugins: ซึ่งช่วยให้ Webpack ระบุและใช้aws-sam-webpack-pluginของเรา
ในบรรทัดแรก เราได้ปิดใช้งานกฎ ESLint เฉพาะสำหรับไฟล์นี้ กฎ ESLint มาตรฐานที่เราจะกำหนดค่าในภายหลัง กีดกันโดยใช้คำสั่ง require เราต้องการ require ให้ import ใน Webpack ดังนั้นเราจะทำการยกเว้น
ตั้งค่าการสนับสนุน TypeScript
การเพิ่มการสนับสนุน TypeScript จะปรับปรุงประสบการณ์ของนักพัฒนาโดย:
- การป้องกันข้อความเตือนเกี่ยวกับการประกาศประเภทที่ขาดหายไป
- ให้การตรวจสอบประเภท
- เสนอการเติมข้อความอัตโนมัติภายใน IDE
ขั้นแรก เราจะติดตั้ง TypeScript สำหรับโครงการของเราในเครื่อง (ข้ามขั้นตอนนี้หากคุณติดตั้ง TypeScript ไว้ทั่วโลก):
npm i --save-dev typescriptเราจะรวมประเภทสำหรับไลบรารีที่เราใช้:
npm i --save-dev @types/node @types/webpack @types/aws-lambda ตอนนี้ เราจะสร้างไฟล์การกำหนดค่า 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 เพื่อหลีกเลี่ยงการคอมไพล์ TypeScript สำหรับโฟลเดอร์ node_modules เราจะไม่แตะต้องโค้ดนี้โดยตรง
สร้างฟังก์ชันแลมบ์ดา
เรายังไม่ได้เขียนโค้ด Lambda สำหรับแอปพลิเคชันแบบไร้เซิร์ฟเวอร์ของเราจนถึงตอนนี้ มาเริ่มกันเลย ในโฟลเดอร์ src ที่เราสร้างไว้ก่อนหน้านี้ เราจะสร้างโฟลเดอร์ย่อย test-lambda ที่มีไฟล์ app.ts ด้วยฟังก์ชัน 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; };ฟังก์ชันตัวยึดตำแหน่งอย่างง่ายนี้ส่งคืนการตอบสนอง 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 ไฟล์เทมเพลตนี้สร้างฟังก์ชัน Lambda ที่สามารถเข้าถึงได้จาก HTTP GET API โปรดทราบว่าเวอร์ชันที่อ้างอิงบนบรรทัด Runtime: อาจต้องมีการปรับแต่ง
เรียกใช้แอปพลิเคชัน
ในการรันแอปพลิเคชัน เราต้องเพิ่มสคริปต์ใหม่ในไฟล์ package.json เพื่อสร้างโครงการด้วย Webpack ไฟล์อาจมีสคริปต์อยู่แล้ว เช่น สคริปต์ทดสอบที่ว่างเปล่า เราสามารถเพิ่ม build script ได้ดังนี้:
"scripts": { "build": "webpack-cli" } หากคุณรัน npm run build จากรูทของโปรเจ็กต์ คุณควรเห็นโฟลเดอร์บิลด์ . .aws-sam สร้างขึ้น พวกเราที่อยู่ในสภาพแวดล้อม Mac อาจจำเป็นต้องทำให้ไฟล์ที่ซ่อนอยู่มองเห็นได้โดยการกด Command + Shift + เพื่อดูโฟลเดอร์
ตอนนี้เราจะเริ่มเซิร์ฟเวอร์ HTTP ในเครื่องเพื่อทดสอบฟังก์ชันของเรา:
sam local start-apiเมื่อเราไปที่จุดสิ้นสุดการทดสอบในเว็บเบราว์เซอร์ เราควรเห็นข้อความแสดงความสำเร็จ
คอนโซลควรแสดงว่ามีการติดตั้งฟังก์ชันในคอนเทนเนอร์ 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ปรับปรุงเวิร์กโฟลว์การพัฒนาของเราสำหรับการตั้งค่าแบบมืออาชีพ
โปรเจ็กต์ของเรากำลังดำเนินการอยู่ การเพิ่มรายละเอียดเล็กๆ น้อยๆ จะช่วยให้นักพัฒนาได้รับประสบการณ์ที่ยอดเยี่ยม ซึ่งจะเพิ่มประสิทธิภาพและการทำงานร่วมกัน

ปรับบิลด์ให้เหมาะสมด้วยการโหลดซ้ำสุดฮอต
การรันคำสั่ง build หลังจากเปลี่ยนโค้ดแต่ละครั้งนั้นน่าเบื่อหน่าย การโหลดซ้ำแบบร้อนจะช่วยแก้ปัญหานี้ได้ เราสามารถเพิ่มสคริปต์อื่นใน package.json เพื่อดูการเปลี่ยนแปลงไฟล์:
"watch": "webpack-cli -w" เปิดเทอร์มินัลแยกต่างหากและเรียกใช้ npm run watch ตอนนี้ โปรเจ็กต์ของคุณจะคอมไพล์โดยอัตโนมัติเมื่อคุณเปลี่ยนโค้ดใดๆ แก้ไขข้อความของรหัส รีเฟรชหน้าเว็บของคุณ และดูผลลัพธ์ที่อัปเดต
ปรับปรุงคุณภาพโค้ดด้วย ESLint และ Prettier
ไม่มีโครงการ TypeScript หรือ JavaScript ใดที่เสร็จสมบูรณ์โดยไม่มี ESLint และ Prettier เครื่องมือเหล่านี้จะรักษาคุณภาพโค้ดของโปรเจ็กต์และความสม่ำเสมอ
มาติดตั้งการพึ่งพาหลักกันก่อน:
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 ต่อไป เราจะเพิ่ม linter ของเราโดยการสร้างไฟล์การกำหนดค่า .eslintrc ภายในรูทของโปรเจ็กต์:
{ "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 เรียกดูกฎที่มีอยู่และปรับแต่งการตั้งค่าเพิ่มเติมได้ตามสบาย เราเลือกที่จะรวม:
- เกิดข้อผิดพลาดหากเราไม่ใช้สตริงที่มีเครื่องหมายอัญประกาศเดี่ยว
- คำเตือนเมื่อเราไม่มีกรณี
defaultในคำสั่งswitch - คำเตือนหากเรากำหนดพารามิเตอร์ของฟังก์ชันใหม่
- คำเตือนหากเราเรียกใช้คำสั่ง
awaitภายในลูป - ข้อผิดพลาดสำหรับตัวแปรที่ไม่ได้ใช้ ซึ่งทำให้โค้ดอ่านไม่ได้และมักเกิดข้อผิดพลาดได้ง่าย
เราได้ตั้งค่าการกำหนดค่า ESLint ให้ทำงานกับการจัดรูปแบบที่สวยงามกว่าแล้ว (ข้อมูลเพิ่มเติมมีอยู่ใน eslint-config-prettier GitHub) ตอนนี้ เราสามารถสร้างไฟล์การกำหนดค่า Prettier ได้ . .prettierrc :
{ "trailingComma": "none", "tabWidth": 4, "semi": true, "singleQuote": true }การตั้งค่าเหล่านี้มาจากเอกสารทางการของพริตเทียร์ คุณสามารถปรับเปลี่ยนได้ตามที่คุณต้องการ เราอัปเดตคุณสมบัติดังต่อไปนี้:
-
trailingComma: เราเปลี่ยนสิ่งนี้จากes5เป็นnoneเพื่อหลีกเลี่ยงเครื่องหมายจุลภาคต่อท้าย -
semi: เราเปลี่ยนสิ่งนี้จากfalsetrueเพราะเราต้องการให้มีอัฒภาคที่ท้ายแต่ละบรรทัด
ในที่สุดก็ถึงเวลาที่จะได้เห็นการทำงานของ ESLint และ Prettier ในไฟล์ app.ts ของเรา เราจะเปลี่ยนประเภทตัวแปร response จาก const เป็น let การใช้ let ไม่ใช่แนวปฏิบัติที่ดีในกรณีนี้ เนื่องจากเราไม่แก้ไขค่าของ response ตัวแก้ไขควรแสดงข้อผิดพลาด กฎที่ใช้งานไม่ได้ และคำแนะนำในการแก้ไขโค้ด อย่าลืมเปิดใช้งาน ESLint และ Prettier ในโปรแกรมแก้ไขของคุณ หากยังไม่ได้ตั้งค่า
รักษารหัสด้วยการทดสอบตลก
ห้องสมุดหลายแห่งพร้อมสำหรับการทดสอบ เช่น 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: ตัวเลือกนี้ช่วยให้เราเขียนการทดสอบใน TypeScript โดยใช้แพ็คเกจts-jest
มาสร้างโฟลเดอร์ __tests__ ใหม่ภายใน src/test-lambda ภายในนั้นเราจะเพิ่มไฟล์ 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 เราควรได้รับการต้อนรับด้วยการทดสอบที่ผ่าน:
จัดการการควบคุมแหล่งที่มาด้วย .gitignore
เราควรกำหนดค่า Git เพื่อแยกไฟล์บางไฟล์ออกจากการควบคุมแหล่งที่มา เราสามารถสร้างไฟล์ .gitignore โดยใช้ gitignore.io เพื่อข้ามไฟล์ที่ไม่จำเป็น:
# 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 .eslintcacheReady, Set, Build: พิมพ์เขียวสู่ความสำเร็จของเรา
ตอนนี้เรามีโครงการสำเร็จรูป AWS SAM พร้อม TypeScript เรามุ่งเน้นที่การทำให้พื้นฐานถูกต้องและรักษาคุณภาพของโค้ดในระดับสูงด้วยการสนับสนุน ESLint, Prettier และ Jest ตัวอย่างจากบทช่วยสอน AWS SAM นี้สามารถทำหน้าที่เป็นพิมพ์เขียว ทำให้โปรเจ็กต์ใหญ่ต่อไปของคุณเป็นจริงตั้งแต่เริ่มต้น
บล็อก Toptal Engineering ขอขอบคุณ Christian Loef สำหรับการตรวจสอบตัวอย่างโค้ดที่นำเสนอในบทความนี้
