artykuły

AWS Lambda z TypeScript i Node.js

Brak komentarzy

AWS Lambda wspiera kilka środowisk uruchomieniowych. W obecnej chwili (lipiec 2020) jest ich aż 6. Ponadto istnieje możliwość implementacji swojego własnego środowiska uruchomieniowego. Jednym ze wspieranych środowisk jest Node.js w wersjach 10 oraz 12. Wybierając to środowisko, musimy przygotować kod naszej aplikacji w języku JavaScript. TypeScript stanowi nadzbiór JavaScript, wprowadzając między innymi klasy znane z innych języków obiektowych czy statyczne typowanie. Chcąc skorzystać z dobrodziejstw języka TypeScript w kontekście AWS Lamba, musimy najpierw zadbać o prawidłowe przygotowanie plików wynikowych.

Z tego artykułu dowiesz się:

  • jakich narzędzi używać w efektywnej pracy z AWS Lambda w połączeniu z TypeScript i Node.js,
  • w jaki sposób tworzyć oraz wdrażać funkcje Lambda, które używają środowiska uruchomieniowego Node.js,
  • jak weryfikować działanie funkcji.

Podczas pracy z narzędziami zintegrowanymi z powłoką systemową oraz pisania skryptów używam PowerShella. Nic nie stoi jednak na przeszkodzie, żeby analogiczne komendy z niewielkim wysiłkiem przenieść do innego środowiska.

Narzędzia przydatne w pracy z AWS Lambda

AWS CLI

Podstawowym narzędziem w pracy z AWS jest AWS CLI. Instrukcję instalacji można znaleźć w oficjalnej dokumentacji. Po instalacji należy je skonfigurować, żeby umożliwić pracę z kontem AWS.

AWS SAM CLI

Kolejnym przydatnym narzędziem jest AWS SAM CLI. Służy do lokalnego uruchamiania funkcji oraz wdrażania ich do chmury. Kompletną instrukcję, w jaki sposób je zainstalować, znajdziesz tutaj.

Struktura projektu

AWS SAM CLI oferuje gotowe szablony, które możemy wykorzystać w tworzeniu aplikacji w środowisku Node.js. Wszystkie szablony bazują jednak na JavaScripcie – zamiast TypeScriptu. Przedstawię, jak w prosty sposób można stworzyć taki szablon i z jakich elementów powinien się składać. To oczywiście tylko propozycja i strukturę można modyfikować według swoich wymagań.

Pierwszym elementem, który powinien się znaleźć w folderze z projektem, jest plik package.json. Możemy w nim znaleźć między innymi zależności naszego projektu oraz skrypty, które będą pomocne podczas programowania, testowania i wdrażania funkcji. Przykładowy, minimalistyczny plik może wyglądać w ten sposób:

{
  "version": "1.0.0",
  "name": "calculate-student-grade",
  "scripts": {
    "rebuild": "(if exist dist rmdir /Q /S dist) && tsc && copy package.json dist\\package.json && (cd dist && yarn install --production)",
    "watch": "tsc --watch"
  },
  "devDependencies": {
    "@types/node": "^13.13.5",
    "typescript": "^3.9.6"
  }
}

Zależności potrzebne tylko w środowisku programistycznym to w minimalnym przypadku typescript oraz @types/node. Pierwsza zależność pozwoli nam na korzystanie z TypeScriptu, natomiast druga dodaje podstawowe typy, z których możemy korzystać.

Warto również zwrócić uwagę na zdefiniowane skrypty. Skrypt rebuild po pierwsze pozwala na transpilację do wynikowego kodu JavaScript. Drugie zadanie, które realizuje, to przygotowanie gotowej paczki z wymaganami zależnościami do poprawnego działania funkcji. Przygotowany w ten sposób folder dist możemy wdrożyć do AWS. Skrypt watch pozwala na śledzenie zmian w kodzie TypeScript i automatyczną transpilację do kodu JavaScript.

Po stworzeniu pliku package.json musimy dodać plik z konfiguracją TypeScriptu – tsconfig.json. Przykładowa wersja takiego pliku może wyglądać następująco:

{
  "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "strict": true,
    "outDir": "./dist",
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

W tym przypadku najważniejszymi ustawieniami są compilerOptions/outDir oraz include. Zakładamy tutaj, że wynikowa paczka, którą będziemy wdrażać na środowisko, będzie równoznaczna z zawartością podkatalogu dist. Z kolei wszystkie źródła napisane w TypeScript będą znajdowały się w podkatalogu src.

Po takim przygotowaniu początkowych ustawień i struktury możemy przejść do pisania kodu źródłowego. Według założeń powinien on znajdować się w podkatalogu src. W tym miejscu tworzymy przede wszystkim plik app.ts, w którym definiujemy punkt wejściowy naszej funkcji Lambda. Bez wchodzenia w szczegóły implementacyjne samej logiki, plik app.ts może wyglądać w następujący sposób:

import { calculateStudentGradeHandler } from "./calculate-student-grade-handler";
import { CalculateStudentGradeResponse } from "./models/calculate-student-grade-response";
import { CalculateStudentGradeRequest } from "./models/calculate-student-grade-request";

export const handler = async (
  request: CalculateStudentGradeRequest
): Promise<CalculateStudentGradeResponse> =>
  calculateStudentGradeHandler(request);

Wdrażanie funkcji

Funkcję Lambda najłatwiej wdrożyć za pomocą AWS SAM oraz CloudFormation. W tym celu musisz zdefiniować plik opisujący funkcję pod kątem infrastrukturalnym. Żeby to osiągnąć, należy stworzyć plik YAML w katalogu projektu. Wewnątrz pliku trzeba zdefiniować wszystkie potrzebne właściwości funkcji:

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Example template for lambda function in Node.js
Resources:
  CalculateStudentGrade:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: calculate-student-grade
      CodeUri: dist
      Handler: app.handler
      Runtime: nodejs12.x
      Timeout: 10
      MemorySize: 128

Zawartość jest dość czytelna i myślę, że większość właściwości nie wymaga komentarza. Pełną dokumentację tego zasobu znajdziesz tutaj. CodeUri wskazuje na lokalizację ostatecznego kodu po transpilacji. Z kolei Handler to (przedzielone kropką) kolejno: nazwa pliku oraz nazwa funkcji, która pełni punkt wejścia do funkcji Lambda.

Posiadając plik opisujący funkcję Lambda, przystąp do wdrożenia. Bardzo pomocne będzie wcześniej wspomniane narzędzie AWS SAM CLI. Zakładając, że wcześniej zostało skonfigurowane konto, na które chcesz wdrożyć funkcję, a także zostały zainstalowane wspomniane wcześniej narzędzia, wystarczy wykonać następujący skrypt w głównym katalogu projektu :

yarn run rebuild
sam deploy `
    --template-file template.yml `
    --stack-name aws-lambda-nodejs `
    --s3-bucket serverless-source-code-bucket `
    --s3-prefix my-stack-prefix/ `
    --region eu-west-1 `
    --capabilities CAPABILITY_IAM `
    --no-fail-on-empty-changeset

Pierwsza komenda przygotowuje kod do wdrożenia, a druga – faktycznie je realizuje. W treści komendy należy zmienić parametr --s3-bucket na nazwę wcześniej stworzonego bucketa, który będzie służył do przechowywania artefaktów wdrożenia. Przy pierwszym wywołaniu skryptu powinniśmy zobaczyć rezultat publikowania projektu oraz pełny proces wdrażania funkcji:

Rezultat wdrożenia stacka CloudFormation z funkcją Lambda

Testowanie działania rozwiązania

Weryfikacja działania w chmurze

Po wdrożeniu funkcji warto zweryfikować jej działanie. Możesz zrobić to, wywołując ją między innymi bezpośrednio z portalu AWS lub z linii poleceń. W celu wywołania funkcji z portalu należy znaleźć ją na liście funkcji i wywołać z poprawnym zapytaniem. Przykładowe zapytanie wykorzystane do wywołania funkcji może wyglądać następująco:

{
    "lectureGrade": 3,
    "exerciseGrade": 4,
    "workshopGrade": 4.5
}
Wynik poprawnego wywołania funkcji

Można również wywołać funkcję z wiersza poleceń za pomocą komendy:

aws lambda invoke `
  --function-name calculate-student-grade `
  --payload file://payload.json `
  response.json

Plik payload.json powinien zawierać wcześniej wspomniane zapytanie, natomiast plik response.json będzie zawierać odpowiedź po wywołaniu funkcji. Po wykonaniu komendy w wierszu poleceń powinna pojawić się informacja o poprawnym wywołaniu funkcji. Z kolei plik response.json powinien zawierać tę samą odpowiedź, co w portalu.

Lokalne uruchamianie funkcji

W celu przetestowania działania funkcji niekoniecznie trzeba wdrażać ją na środowisko. Dzięki AWS SAM CLI możesz uruchomić funkcję lokalnie. Żeby to zrobić, wystarczy wywołać odpowiednią komendę:

sam local invoke -e payload.json CalculateStudentGrade

Po wykonaniu komendy zostanie przygotowany kontener, który obsługuje nasze zapytanie. Otrzymujemy pełny log wywołania, razem z odpowiedzią z funkcji.

Rezultat lokalnego wywołania funkcji Lambda

Możemy nie tylko uruchamiać lokalnie naszą funkcję, ale również ją debuggować. Bardzo przydatny do tego celu jest edytor kodu Visual Studio Code. Jedyne, co musimy przygotować, to odpowiednia konfiguracja, która powinna znaleźć się w folderze .vscode, w pliku launch.json. Plik powinien wyglądać następująco:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to SAM CLI",
      "type": "node",
      "request": "attach",
      "address": "localhost",
      "port": 5858,
      "localRoot": "${workspaceRoot}/dist",
      "remoteRoot": "/var/task",
      "protocol": "inspector",
      "stopOnEntry": false
    }
  ]
}

Cały proces debugowania najlepiej zobaczyć bezpośrednio w akcji:

Wszystkie przykłady gotowe do samodzielnego przetestowania znajdziesz w naszym repozytorium.

Tags: , , ,

Powiązane artykuły

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Wypełnij to pole
Wypełnij to pole
Proszę wprowadzić prawidłowy adres email.
You need to agree with the terms to proceed

Menu