Работа с поддержкой TypeScript и Jest: учебное пособие по AWS SAM

Опубликовано: 2022-03-11

Модель бессерверных приложений (SAM) AWS, мощный инструмент для создания бессерверных приложений, часто сочетается с JavaScript: 62 % разработчиков в средних и крупных компаниях выбирают JavaScript для своего бессерверного кода. Тем не менее, TypeScript стремительно набирает популярность и намного превосходит JavaScript как третий по популярности язык разработчиков.

Хотя шаблонный шаблон JavaScript найти несложно, запускать проекты AWS SAM с помощью TypeScript сложнее. В следующем руководстве показано, как создать проект AWS SAM TypeScript с нуля, а также как различные части работают вместе. Читатели должны быть лишь немного знакомы с функциями AWS Lambda, чтобы следовать им.

Запуск нашего проекта AWS SAM TypeScript

В основе нашего бессерверного приложения лежат различные компоненты. Сначала мы настроим среду AWS, наш пакет npm и функциональность Webpack, а затем сможем создать, вызвать и протестировать нашу функцию Lambda, чтобы увидеть наше приложение в действии.

Подготовьте среду

Чтобы настроить среду AWS, нам необходимо установить следующее:

  1. Интерфейс командной строки AWS
  2. Интерфейс командной строки AWS SAM
  3. Node.js и нпм

Обратите внимание, что в этом руководстве требуется установить Docker на шаге 2 выше, чтобы протестировать наше приложение локально.

Инициализировать пустой проект

Давайте создадим каталог проекта, aws-sam-typescript-boilerplate и подпапку src для хранения кода. Из каталога проекта мы настроим новый пакет npm:

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

Эта команда создаст файл package.json внутри нашего проекта.

Добавьте конфигурацию веб-пакета

Webpack — это сборщик модулей, который в основном используется для приложений JavaScript. Поскольку TypeScript компилируется в обычный JavaScript, Webpack эффективно подготовит наш код для веб-браузера. Мы установим две библиотеки и собственный загрузчик:

  • веб-пакет: основная библиотека
  • webpack-cli: Утилиты командной строки для Webpack
  • ts-loader: загрузчик 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 : загружает объект entry (где Webpack начинает сборку пакета) из ресурса AWS::Serverless::Function .
  • 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 в качестве нашей среды. Это означает, что require будет использовать функцию запроса Node.js для загрузки фрагментов при компиляции.
  • module : это применяет загрузчик TypeScript ко всем файлам, которые соответствуют условию test . Другими словами, это гарантирует, что все файлы с расширением .ts или .tsx будут обработаны загрузчиком.
  • plugins : это помогает Webpack идентифицировать и использовать наш aws-sam-webpack-plugin .

В первой строке мы отключили конкретное правило ESLint для этого файла. Стандартные правила ESLint, которые мы настроим позже, не рекомендуют использовать оператор require . Мы предпочитаем import в require , поэтому сделаем исключение.

Настройте поддержку 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 , чтобы избежать компиляции 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. В файле могут быть существующие сценарии, например пустой тестовый сценарий. Мы можем добавить скрипт сборки следующим образом:

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

Если вы запустите npm run build из корня проекта, вы должны увидеть созданную папку сборки .aws-sam . Тем из нас, кто работает в среде Mac, может потребоваться сделать скрытые файлы видимыми, нажав Command + Shift + . чтобы увидеть папку.

Теперь мы запустим локальный HTTP-сервер для проверки нашей функции:

 sam local start-api

Когда мы посещаем конечную точку теста в веб-браузере, мы должны увидеть сообщение об успешном завершении.

Веб-браузер показывает ссылку «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

Ни один проект 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

Далее мы добавим наш линтер, создав файл конфигурации .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"] } } } }

Обратите внимание, что раздел extensions нашего файла должен содержать конфигурацию плагина extends в качестве последней строки, чтобы отображать ошибки Prettier как ошибки ESLint, видимые в нашем редакторе. Мы следуем рекомендуемым ESLint настройкам для TypeScript с некоторыми пользовательскими настройками, добавленными в раздел rules . Не стесняйтесь просматривать доступные правила и дополнительно настраивать параметры. Мы решили включить:

  • Ошибка, если мы не используем строки в одинарных кавычках.
  • Предупреждение, когда мы не указываем регистр по default в операторах switch .
  • Предупреждение, если мы переназначаем любой параметр функции.
  • Предупреждение, если мы вызываем оператор 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». Рядом с линией отображается желтая лампочка, а слово «ответ» подчеркнуто красным, а над ним всплывает сообщение об ошибке. Всплывающее окно с ошибкой сначала определяет переменную «ответ» и гласит: «пусть ответ: {statusCode: number; body: string; }». Под определением сообщение об ошибке гласит: «Ответ» никогда не переназначается. Вместо этого используйте «const». eslint(prefer-const)». Под сообщением об ошибке есть два варианта: «Просмотреть проблему» или «Быстрое исправление».

Поддерживайте код с помощью Jest Testing

Многие библиотеки доступны для тестирования, такие как 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 , нас должен приветствовать пройденный тест:

В верхней части консоли отображается зеленый индикатор «Пройдено» и имя тестового файла «src/test-lambda/__tests__/handler.test.ts». Следующая строка гласит: «Демонстрационный тест». В следующей строке отображается зеленая галочка, за которой следует «Это доказательство того, что тест работает. (1 мс)». После пустой строки первая строка гласит: «Наборы тестов: 1 пройдено, 1 всего». Второй гласит: «Тесты: 1 пройдено, 1 всего». Третий гласит: «Снимков: всего 0». Четвертый гласит: «Время: 0,959 с». Последняя строка гласит: «Выполнены все наборы тестов».

Управление исходным кодом с помощью .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

Готово, установлено, построено: наш план успеха

Теперь у нас есть полный шаблонный проект AWS SAM с TypeScript. Мы сосредоточились на правильном понимании основ и поддержании высокого качества кода с поддержкой ESLint, Prettier и Jest. Пример из этого руководства по AWS SAM может послужить образцом для планирования вашего следующего крупного проекта с самого начала.

Блог Toptal Engineering выражает благодарность Кристиану Лоэфу за рассмотрение примеров кода, представленных в этой статье.

Логотип AWS со словом «PARTNER» и текстом «Advanced Tier Services» под ним.
В качестве продвинутого партнера-консультанта в партнерской сети Amazon (APN) Toptal предлагает компаниям доступ к сертифицированным AWS экспертам по запросу в любой точке мира.