🔮

Typescript開発である自分がAWS Lambdaを開発するにあたって知っておきたかったこと

2024/01/08に公開

Typescriptでバックエンド、モバイルアプリの開発経験のある僕がAWSのサーバーレスサービスを活用するに当たって知っておきたかったことを紹介させていただきます。

最初に結論を言うと以下の5つです。

  1. タイムアウト値を確認しましょう
  2. 処理時間が長いのはコールドスタートが原因
  3. LambdaはTypescriptで実装可能です
  4. 公式が提供しているSDKを活用しましょう
  5. 何らかのIaCを活用すると開発体験が劇的に改善します

タイムアウト値を確認しましょう

開発中に実行エラーが発生すると、実装が誤っているわけではないかもしれません。タイムアウト値を超過している可能性があります。設定画面からタイムアウト値を確認・調整してみましょう。「設定」->「一般設定」からタイムアウト値を変更しましょう。

処理時間が長いのはコールドスタートが原因

Lambdaを初回実行、もしくはしばらく時間を置いてから実行すると通常より処理時間が長い時があります。これはコールドスタートと言ってLambdaを実行するための環境を用意するのに時間がかかっているためです。

もし、コールドスタートがどうしても気になる場合は、解決策は2つ用意されています。

  1. プロビジョニングされた同時実行を設定する
  2. 定期的にLambdaを実行する。(非推奨)

LambdaはTypescriptで実装可能

「AWS Lambda Nodejs (実装したいこと)」で検索するとjavascriptでLambdaを実装する方法が上位にヒットするため、LambdaがJavascriptのみで実装可能か疑問に思ったことはありませんか?Esbuildなどのトランスパイラーを利用すれば、Typescriptで実装したコードもLambdaで実行可能になります。

https://docs.aws.amazon.com/lambda/latest/dg/typescript-package.html

これで普段使い慣れている構文を用いてLambda関数を実装できます。しかしそれだけではありません。

公式が提供しているSDKを活用しましょう

AWSはLambdaの開発をサポートするために様々なSDKを提供しています。これらを利用することで、IDEを使って手軽にS3へのアクセスやDynamoDBへの書き込みを実装できます。以下では、私が特に便利だと感じているいくつかのライブラリを紹介します。

@types/aws-lambda

https://www.npmjs.com/package/@types/aws-lambda

@types/aws-lambdaを利用すると、Lambdaのeventや返り値にanyではなく、適切な型を指定することができます。

import { APIGatewayEvent, ProxyResult } from 'aws-lambda'

export const handler = async (event: APIGatewayEvent): Promise<ProxyResult> => {
  const name = event.queryStringParameters?.name || 'World'
  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      message: `Hello ${name}!`,
    }),
  }
}

util-dynamodb

https://www.npmjs.com/package/@aws-sdk/util-dynamodb

util-dynamodbは、JavascriptオブジェクトからDynamoDBレコードへの変換を担当するmarshallやその逆の変換であるunmarshallを提供しています。これを利用することで、手動でのオブジェクトからレコードへの変換が不要となり、開発が格段に便利になります。

import {
  DynamoDBClient,
  PutItemCommand,
} from '@aws-sdk/client-dynamodb'
import { marshall } from '@aws-sdk/util-dynamodb'


const todo = {
  description: "記事を書く",
  isDone: true
}

const client = new DynamoDBClient({})

await client.send(new PutItemCommand({
  TableName: 'Todos',
  Item: marshall(todo) // 自前でDynamoDBレコードへの変換が不要
})

aws-sdk-js-v3

https://github.com/aws/aws-sdk-js-v3

aws-sdk-js-v3では各種AWSサービスへのクライアントが提供されています。もしLambda側で呼びたいサービスがあればまずここを探してみましょう。

何らかのIaCを活用すると開発体験が劇的に改善します

「AWS Lambda Nodejs」で検索すると、以下の手順を用いてデプロイする方法をよく見かけます

  1. 手元で開発
  2. zipファイルで固める
  3. Lambdaコンソール画面右ある、「アップロード元」から「.zipファイル」を指定して2.のzipファイルを指定する

Lambdaがどのようなものか知りたい、だけであればそれで十分かもしれませんが、実際に開発を行うと、コードをデプロイする度に1から3の手順を手動で行うのは割と時間がかかるし、ヒューマンエラーが起こりやすいです。

コマンド一発でデプロイしたい、それを実現するのがIaC(Infrastrucre as Code)です。IaCとはインフラストラクチャの設定や管理をコードとして扱うアプローチであり、アプリ開発と同様に、AWSのインフラストラクチャ(Lambda等)をコードで管理するという手法です。

AWSでのIaCツールの選択は、個々のプロジェクトや要件によって異なります。以下は代表的な選択肢です。

Terraformは私が所属する組織で採用されており、以下はその利点とデメリットについての感想です。

利点

  • 社内での知見が豊富で、問題が発生した際に助けを求めやすいです。
  • 学習コストが低く、基本的な操作や構文は簡単に理解できます。
  • 定義したインフラストラクチャのみが利用されるため、不要なコストがかかりにくいです。

デメリット

  • IAMポリシー周りなど、一部の詳細な設定が手間となることがあります。
  • AWSの新しい機能や変更に対応するまでに時間がかかることがあります。

まとめ

  • Lambdaのタイムアウト値は確認しましょう
  • 実行が通常より遅いと感じたら、それはおそらくコールドスタートが原因です
  • Typescriptを用いたLambda関数の実装はEsbuildなどのトランスパイラーを利用することで容易に行えます。
  • AWSが提供する各種SDKの活用により、S3やDynamoDBなどのサービスへのアクセスが簡素化され、開発プロセスが効率化されます。
  • インフラストラクチャのコード化(IaC)を導入することで、デプロイプロセスが自動化され、ヒューマンエラーが軽減されます。Terraformを選択する際は、社内での知見と学習難易度の低さを活かしましょう。

参考文献

https://qiita.com/ny7760/items/700ae917da2c5b5e3f8a#どちらで対応すべきか

https://aws.amazon.com/jp/blogs/compute/optimizing-node-js-dependencies-in-aws-lambda/

https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-nodejs.html

Discussion