Serverless Framework + TypeScriptで型情報を頼りにSQSからLambdaを呼び出すLambda関数を作る
Serverless Framework + TypeScriptはいいぞ。
Serverless Framework で Serverless 開発環境が簡単に構築できるし、TypeScript によって型に守られたプログラミングができるのだ!
Serverless Frameworkのインストール
curl でインストールシェルスクリプトを落として実行する方法や、Chocolatey を使ったインストール方法や、npmからのインストール方法がドキュメントに記載されています。
ここではnpmからインストールする方法でやっていきます。
npm install -g serverless
バージョン確認を行ってどのバージョンがインストールできたか確かめてみましょう。
serverless -v
# Framework Core: 1.65.0
# Plugin: 3.4.1
# SDK: 2.3.0
# Components: 2.22.3
バージョンが確認できればインストール完了です!
プロジェクトを作成する
Serverless Framework ではすでに用意されているテンプレートを使用してプロジェクトを作成することができます。
テンプレートには大雑把に「クラウド環境 * 言語」が用意されています。
serverless create --help
上記で確認できるテンプレート一覧は現時点でこれだけありました。
"aws-clojure-gradle", "aws-clojurescript-gradle", "aws-nodejs", "aws-nodejs-typescript", "aws-alexa-typescript", "aws-nodejs-ecma-script", "aws-python", "aws-python3", "aws-groovy-gradle", "aws-java-maven", "aws-java-gradle", "aws-kotlin-jvm-maven", "aws-kotlin-jvm-gradle", "aws-kotlin-nodejs-gradle", "aws-scala-sbt", "aws-csharp", "aws-fsharp", "aws-go", "aws-go-dep", "aws-go-mod", "aws-ruby", "aws-provided", "tencent-go", "tencent-nodejs", "tencent-python", "tencent-php", "azure-nodejs", "azure-nodejs-typescript", "azure-python", "cloudflare-workers", "cloudflare-workers-enterprise", "cloudflare-workers-rust", "fn-nodejs", "fn-go", "google-nodejs", "google-python", "google-go", "kubeless-python", "kubeless-nodejs", "knative-docker", "openwhisk-java-maven", "openwhisk-nodejs", "openwhisk-php", "openwhisk-python", "openwhisk-ruby", "openwhisk-swift", "spotinst-nodejs", "spotinst-python", "spotinst-ruby", "spotinst-java8", "twilio-nodejs", "aliyun-nodejs", "plugin", "hello-world"
今回は AWS Lambda で TypeScript を使うので、aws-nodejs-typescript テンプレートを使用します。
serverless create コマンドでプロジェクトの作成を行います。
-t(もしくは--template)の後に使用するテンプレートを指定します。
-n(もしくは--name)の後にプロジェクト名を指定します。
-p(もしくは--path)の後にプロジェクト作成ディレクトリを指定します。
sample-sqs-lambda という名前でプロジェクトを作成してみます。
serverless create -t aws-nodejs-typescript -n sample-sqs-lambda -p sample-sqs-lambda
-p/--path でディレクトリを指定しないと、カレントディレクトリにファイルが生成されるので注意しましょう。
それでは Visual Studio Code でコードの編集をしていきます。
code sample-sqs-lambda
ファイル構成はこのようになっています。
handler.ts がエントリポイントのファイルになっているのですが、編集する前に必要なパッケージをインストールします。
cd sample-sqs-lambda
npm install
API GatewayからSQSに変更する
初期状態の handler.ts は以下のようになっています。4行目でAPIGatewayProxyHandler 型を使っているので、API Gateway + Lambda のプロジェクトになっていることが分かりますね。
import { APIGatewayProxyHandler } from 'aws-lambda';
import 'source-map-support/register';
export const hello: APIGatewayProxyHandler = async (event, _context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',
input: event,
}, null, 2),
};
}
SQSから呼ばれるようにするにはどうすればいいのでしょうか?
APIGatewayProxyHandler が API Gateway というサービスで Lambda Proxy を使ったHandlerを表していることが推測できます。つまり、SQS というサービス名の Handler を表した型があればそれが使えそうです。
おもむろに SQS と入力して IntelliSense に任せてみると……
そのまんまな SQSHandler 型がありますね。IntelliSense に任せることで import も自動で行ってくれます。
APIGatewayProxyHandler から SQSHandler に変更したことで、hello でエラーを示す赤線が引かれています。そこにマウスポインタを持っていくとエラー詳細を見ることができます。
型 '(event: SQSEvent, _context: Context) => Promise<{ statusCode: number; body: string; }>' を型 'Handler<SQSEvent, void>' に割り当てることはできません。
型 'Promise<{ statusCode: number; body: string; }>' を型 'void | Promise<void>' に割り当てることはできません。
型 'Promise<{ statusCode: number; body: string; }>' を型 'Promise<void>' に割り当てることはできません。
型 '{ statusCode: number; body: string; }' を型 'void' に割り当てることはできません。ts(2322)
戻り値の型が違うと怒られています。
今までは API Gateway にステータスコードとメッセージ内容を戻り値として返す必要があったのですが、 SQS から呼ばれる場合は SQSに戻り値を返す必要がないためです。
なので return文を消してやればエラーは解消できます。ついでに、importの APIGatewayProxyHandler も使用していないので消しましょう。
現時点のソースコードは以下のようになりました。
import { SQSHandler } from "aws-lambda";
import "source-map-support/register";
export const hello: SQSHandler = async (event, _context) => {};
これで完成としてしまうとトマソンばりの何のためかよく分からないものができあがってしまうので、最低限 SQS から渡された文字列をログに出力するようにしてみましょう。
SQS から渡された情報は、SQSEvent 型の引数 event に格納されています。event が何を持っているか調べるためにevent.と打ってみましょう。
SQSRecord 型の配列が格納されている Records の存在が確認できます。その Records を一個ずつ処理してやればいい感じに SQS から渡された情報が取得できそうです。
さきほどと同じように、event.Records. と打ってみると……。
forEach で回せそうです。
イマココ
import { SQSHandler } from "aws-lambda";
import "source-map-support/register";
export const hello: SQSHandler = async (event, _context) => {
event.Records.forEach(record => {
});
};
またまた同じように、record. と打って何を持ってるか見てみます。
名前的に body が SQS から渡されたメッセージ本文が入っていそうなことが推測できます。それではこの body をログに出力してみましょう。標準出力を使えば勝手にログに吐いてくれます。
完成形がこちら!
import { SQSHandler } from "aws-lambda";
import "source-map-support/register";
export const hello: SQSHandler = async (event, _context) => {
event.Records.forEach(record => {
console.log(record.body);
});
};
型情報を頼りにドキュメントを見ることなく、ここまで作ることができました。
SQSキューを作成する
Serverless Framework では SQS キューを作成することはできません。なので事前に SQS キューを作成しておく必要があります。 ※訂正記事あり※
Note: The sqs event will hook up your existing SQS Queue to a Lambda function. Serverless won't create a new queue for you.
https://serverless.com/framework/docs/providers/aws/events/sqs/#sqs-queues
※作れます!
AWS CDK で TypeScript を使ってインフラ構築してもいいですし、AWS SDK やコンソールでもなんでもいいです。
sample-sqs-lambda-queue という名前の標準キューをデフォルト設定のまま使います。
キュー名を入力してクイック作成でOKです。
SQSキューからLambdaを呼び出す設定をする
serverless.yml を編集し、SQS キューをトリガーに先ほど作成した Lambda 関数を呼び出すように設定していきます。
初期状態では API Gateway で呼び出されるようになっています。
functions:
hello:
handler: handler.hello
events:
- http:
method: get
path: hello
これを、以下のように変更します。
functions:
hello:
handler: handler.hello
events:
- sqs: arn:aws:sqs:us-east-1:xxxxxxxxxxxx:sample-sqs-lambda-queue
sqs: の後に先ほど作成した SQS キューの ARN を指定してください。これだけでデプロイ時に SQS キューのトリガーに Lambda 関数が登録されます。
これにて Lambda 関数の作成と SQS キューの作成と、トリガーの設定が完了しました。あとはデプロイして実際に確認してみましょう。
デプロイしよう
AWS CLI の導入や IAM ユーザの作成や credential の設定は完了している前提で話を進めていきます。
デプロイは serverless deploy コマンドを使用して行います。オプションを指定せず実行すれば開発環境としてデプロイします。
serverless deploy
Serverless: Bundling with Webpack...
Time: 669ms
Built at: 2020-03-08 16:40:42
Asset Size Chunks Chunk Names
handler.js 1.19 KiB 0 [emitted] handler
handler.js.map 5.1 KiB 0 [emitted] [dev] handler
Entrypoint handler = handler.js handler.js.map
[0] ./handler.ts 177 bytes {0} [built]
[1] external "source-map-support/register" 42 bytes {0} [built]
Serverless: Package lock found - Using locked versions
Serverless: Packing external modules: source-map-support@^0.5.10
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service sample-sqs-lambda.zip file to S3 (288.95 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..................
Serverless: Stack update finished...
Service Information
service: sample-sqs-lambda
stage: dev
region: us-east-1
stack: sample-sqs-lambda-dev
resources: 7
api keys:
None
endpoints:
None
functions:
hello: sample-sqs-lambda-dev-hello
layers:
None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
このコマンドを叩くだけで、TypeScript が JavaScript にトランスコンパイルし、依存関係をまとめた zip ファイルを作成し、S3 にアップロードし、CloudFormation を使って Lambda 関数用 IAM ユーザの作成と、Lambda 関数のデプロイと、SQS キューをトリガーに起動する設定までしてくれます。これぞ Infrastructure as Code !
実際に AWS コンソールで存在するか見てみましょう。
検証環境として dev が付いた状態でデプロイされているのが確認できました。
実際に動かしてみよう
SQS キューにメッセージを送信し、Lambda 関数のログに出力されているかを確認します。SQS キューを選択し、メッセージの送信をクリックします。
メッセージ本文に何らかの文字列を入力し、メッセージの送信ボタンを押します。
ログは Cloud Watch のロググループで確認できます。
ロググループの中にログストリームがあり、そこにログが出力されているでしょう。
SQS キューにメッセージを送信すると、それをトリガーに Lambda 関数が起動し、メッセージを処理できることが確認できました!
後片付け
これ以上使うことはないので、不用意な課金を防ぐために用意したリソースを削除しましょう。削除は serverless remove コマンドを用います。
serverless remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
.............
Serverless: Stack removal finished...
事前準備したのと同じく、SQS キューは自分で削除する必要があるので消し忘れに注意しましょう。
まとめ
✔ Serverless Framework は簡単に導入することができる
✔ Serverless Framework は複数のクラウド環境や言語に対応している
✔ Serverless Framework で簡単に Lambda 関数が作成できる
✔ TypeScript で型情報を頼りに API Gateway から SQS に変更した
✔ IntelliSense の補完を頼りにプログラミングした
✔ Serverless Framework で Infrastructure as Code をした
✔ ただし SQS キューの作成まではしてくれないので事前準備が必要
✔ SQS キューをトリガーに Lambda 関数を起動することを確認した
✔ Serverless Framework + TypeScript はいいぞ!
😉