العمل مع دعم TypeScript و Jest: برنامج تعليمي لـ AWS SAM
نشرت: 2022-03-11يُعد AWS Serverless Application Model (SAM) أداة قوية لبناء تطبيقات بدون خادم ، ويقترن كثيرًا مع JavaScript: يختار 62٪ من المطورين عبر الشركات المتوسطة والكبيرة JavaScript للتعليمات البرمجية بدون خادم. ومع ذلك ، تزداد شعبية TypeScript وتتفوق كثيرًا على JavaScript باعتبارها ثالث أكثر اللغات المحببة للمطورين.
بينما ليس من الصعب العثور على JavaScript boilerplate ، فإن بدء مشروعات 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-cli: أدوات مساعدة لسطر الأوامر لـ Webpack
- محمل ts: محمل TypeScript لـ Webpack
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: يؤدي هذا إلى تحميل كائن الإدخال (حيث يبدأ Webpack في إنشاء الحزمة) منAWS::Serverless::FunctionResource. -
output: يشير هذا إلى وجهة مخرجات البناء (في هذه الحالة ،.aws-sam/build). هنا نحدد أيضًا المكتبة الهدف على أنهاcommonjs2، والتي تعين قيمة الإرجاع لنقطة الإدخال إلىmodule.exports. نقطة الإدخال هذه هي نقطة الدخول الافتراضية لبيئات Node.js. -
devtool: يؤدي هذا إلى إنشاء خريطة مصدر ،app.js.map، في وجهة مخرجات البناء الخاصة بنا. يقوم بتعيين الكود الأصلي الخاص بنا إلى الكود الذي يتم تشغيله في متصفح الويب وسيساعد في تصحيح الأخطاء إذا قمنا بتعيين متغير البيئةNODE_OPTIONSعلى--enable-source-mapsلـ Lambda. -
resolve: هذا يخبر Webpack بمعالجة ملفات TypeScript قبل ملفات JavaScript. -
target: هذا يخبر Webpack باستهداف Node.js كبيئتنا. هذا يعني أن Webpack سيستخدم وظيفةrequireNode.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 الآن ، سننشئ ملف تكوين 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 لمجلد node_modules - لن نلمس هذا الرمز مباشرةً.
قم بإنشاء دالة Lambda
لم نكتب أي كود 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. لاحظ أن الإصدار المشار إليه في Runtime: line قد يحتاج إلى التخصيص.
قم بتشغيل التطبيق
لتشغيل التطبيق ، يجب علينا إضافة برنامج نصي جديد في ملف package.json لبناء المشروع باستخدام Webpack. قد يحتوي الملف على نصوص حالية ، مثل نص اختبار فارغ. يمكننا إضافة نص البناء مثل هذا:
"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تعزيز سير عمل التطوير لدينا لإعداد احترافي
مشروعنا جاهز للعمل ، وستضمن إضافة بعض اللمسات النهائية تجربة مطور استثنائية من شأنها تعزيز الإنتاجية والتعاون.

قم بتحسين الإنشاء باستخدام إعادة التحميل السريع
من الممل تشغيل أمر الإنشاء بعد كل تغيير في التعليمات البرمجية. إعادة التحميل السريع سيصلح هذه المشكلة. يمكننا إضافة برنامج نصي آخر في package.json الخاص بنا لمشاهدة تغييرات الملف:
"watch": "webpack-cli -w" افتح محطة منفصلة وقم npm run watch . الآن ، سيتم تجميع مشروعك تلقائيًا عند تغيير أي رمز. قم بتعديل رسالة الرمز ، وقم بتحديث صفحة الويب الخاصة بك ، واطلع على النتيجة المحدثة.
تحسين جودة الكود مع ESLint وأجمل
لا يكتمل أي مشروع 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 عن طريق إنشاء ملف تكوين ESLint ، .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 كأخطاء ESLint مرئية في محررنا. نحن نتبع إعدادات ESLint الموصى بها لـ TypeScript ، مع إضافة بعض التفضيلات المخصصة في قسم rules . لا تتردد في تصفح القواعد المتاحة وتخصيص إعداداتك بشكل أكبر. اخترنا تضمين:
- خطأ إذا لم نستخدم سلاسل ذات علامات اقتباس مفردة.
- تحذير عندما لا نقدم حالة
defaultفي عباراتswitch. - تحذير إذا قمنا بإعادة تعيين أي معلمة للدالة.
- تحذير إذا استدعينا تعليمة
awaitداخل حلقة. - خطأ للمتغيرات غير المستخدمة ، مما يجعل الشفرة غير قابلة للقراءة وعرضة للأخطاء بمرور الوقت.
لقد قمنا بالفعل بإعداد تكوين ESLint الخاص بنا للعمل مع تنسيق أجمل. (يتوفر المزيد من المعلومات في مشروع 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 على المحرر الخاص بك إذا لم يتم إعدادهما بالفعل.
الحفاظ على الكود مع اختبار الدعابة
تتوفر العديد من المكتبات للاختبار ، مثل 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: تتضمن هذه المجموعة من أنماط الكرة الأرضية امتدادات الملفات التي سيتم اعتبارها ملفات 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 .eslintcacheجاهز ، جاهز ، أنشئ: مخططنا للنجاح
لدينا الآن مشروع Boilerplate AWS SAM كامل مع TypeScript. لقد ركزنا على الحصول على الأساسيات بشكل صحيح والحفاظ على جودة الأكواد العالية مع دعم ESLint و Prettier و Jest. يمكن أن يكون المثال من هذا البرنامج التعليمي AWS SAM بمثابة مخطط ، يضع مشروعك الكبير التالي على المسار الصحيح من البداية.
تعرب مدونة Toptal Engineering عن امتنانها لكريستيان لوف لمراجعة نماذج التعليمات البرمجية المقدمة في هذه المقالة.
