Lambda Function URLでLambdaをHTTPで直接実行する

| 6 min read
Author: noboru-kudo noboru-kudoの画像

2022/04/06 AWSはLambdaをHTTPで直接呼び出す機能追加を発表しました。

今まではLambdaをHTTPベースで呼び出す場合は、API GatewayやALB(Application Load Balancer)を経由する必要がありましたが、Lambda Function URLという新たな選択肢ができました。
サーバーレス環境のプロビジョニングツールのServerless Frameworkも、すぐにv3.12.0からLambda Function URLに対応しました。

今回はこの新機能を試してみます。

Contents

事前準備

#

Serverless Frameworkは、現時点で最新の3.13.0を使用します。

npm install -g serverless@3.13.0
serverless --version
Framework Core: 3.13.0
Plugin: 6.2.1
SDK: 4.3.2

プロジェクト作成

#

Serverless FrameworkのCLIで作成します。今回はaws-nodejsのテンプレートを使用します。

serverless create --template aws-nodejs \
--path lambda-function-url

lambda-function-urlディレクトリが作成され、サンプルのLambdaとserverless.ymlが配置されます。
ここで作成されるサンプルLambdaを、API Gateway経由でなくLambda Function URLを使うように設定します。

Lambda Function URL有効化

#

Serverless Frameworkで作成されたlambda-function-url/serverless.ymlを編集して、Lambda Function URLに変更します。

provider:
name: aws
# Node14+東京リージョン
runtime: nodejs14.x
region: ap-northeast-1
functions:
hello:
handler: handler.hello
# Lambda Function URL使用
url: true

重要な変更点はurl: trueです。
API Gatewayを使う場合はevents.http配下にAPI Gatewayのパス等の設定を記述していましたが、Lambda Function URLはこれだけで十分です。

CloudFormationテンプレートの確認

#

後はデプロイするだけですが、せっかくなのでServerless Frameworkが生成するCloud Formationのテンプレートも見てみます。

serverless package

lambda-function-url/.serverless/cloudformation-template-update-stack.jsonのCloud Formationのテンプレートを確認します。
以下該当部分のみを抜粋します。

{
"HelloLambdaFunctionUrl": {
"Type": "AWS::Lambda::Url",
"Properties": {
"AuthType": "NONE",
"TargetFunctionArn": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
}
}
}
}

CloudFormationリソースとしてAWS::Lambda::Urlが追加されているのが確認できます。
今回は認証設定をしていないのでNONEとなっていますが、IAM認証もできます(Cognito等はできません)。
CloudFormationテンプレートの詳細は公式ドキュメントを参照してください。

Serverless Frameworkでデプロイ

#

では、これをデプロイしてみます。

serverless deploy
Deploying lambda-function-url to stage dev (ap-northeast-1)

✔ Service deployed to stack lambda-function-url-dev (117s)

endpoint: https://xxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/
functions:
  hello: lambda-function-url-dev-hello (392 B)

Serverless Frameworkの出力結果にエンドポイントが表示されています。公式ドキュメントによると、Lambda Function URLは以下の形式でURLが生成されるようです。
これはLambdaを再作成しなければ変わることはありません。

https://<url-id>.lambda-url.<region>.on.aws

このエンドポイントにcurlでアクセスしてみます。

curl https://xxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": {
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/",
# 以下省略

API Gatewayなしで直接Lambdaにアクセスできることが分かります。
レスポンスの形式もLambdaで返しているフォーマットではなく、通常のREST APIアクセスと変わりません(サンプルのLambdaはeventの中身も出力しているので見にくいですが)。

ドキュメントを確認すると、Lambda上のI/FはAPI Gateway(Lambda Proxy)互換のリクエスト・レスポンスフォーマットをそのまま利用できます。
実際のクライアントリクエスト、レスポンスのマッピングはLambda Function URLが行います。
以下の公式ドキュメントにマッピングの詳細が記載されていますので、興味のある方は参照してください。

AWS マネジメントコンソールでは、以下のようにLambda Function URLのエンドポイントや設定を確認できます。

CORSを設定する

#

Lambda Function URLはランダムなドメインが割り当てられますので、ブラウザから直接アクセスする場合はCORS対策が欠かせません。
Lambda Function URLはCORSにも対応していますので、これを試してみます。
serverless.ymlを以下のように修正します。

functions:
hello:
handler: handler.hello
url:
# Lambda Function URLのCORS設定
cors:
allowedOrigins: https://www.example.com
allowedMethods: PUT
allowedHeaders: Content-Type,Authorization
allowCredentials: true

corsをオブジェクト型として、CORSの各種ヘッダを設定します。
設定可能なCORSヘッダはLambda Function URLの公式ドキュメントを参照してください。
これでデプロイして、curlでpre-flightリクエスト(OPTIONSメソッド)をエミュレートしてみます。

curl -X OPTIONS --head \
-H 'Origin: https://www.example.com' \
-H 'Access-Control-Request-Method: PUT' \
https://xxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/
HTTP/1.1 200 OK
Date: Thu, 14 Apr 2022 05:32:06 GMT
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
x-amzn-RequestId: d78cb417-d306-455e-ac1e-deafa1bf04a8
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Headers: content-type,authorization
Vary: Origin
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Credentials: true

CORS関連のレスポンスヘッダが、Lambda Function URLによって付与されていることが分かります。

このCORSの設定は、マネジメントコンソール上でも確認できます。

まとめ

#

今までLambdaをHTTPアクセスするためには、API Gateway等の別のリソースを使う必要がありましたが、Lambda Function URLを使うとLambda単体でお手軽にサーバーレス環境を構築できるようになりました。

ただし、Lambda Function URLはAPI Gatewayを置き換えるものではありません。
API Gatewayのような高度なマッピングやキャッシュ、IAM以外の認証は基本できません(Lambda内で頑張るしかない)。

Serverless Frameworkの公式ブログに適切なユースケースについて言及されていますのでご参考ください。

  • Express.js等のフレームワークを使ってLambda内でリクエストルーティングをするケース(単一関数)
  • シンプルなWebHook
  • SSRのバックエンド(Nuxt.js, Next.js等)
  • API Gatewayの29秒タイムアウトを超えるAPI(Lambdaの最大タイムアウト15分を適用可)

Lambda Function URLのHTTPはあくまで同期通信で、システム間連携等で乱用するとシステム全体の可用性を下げる結果となりますので注意したいところです。


参照資料

豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 自然言語処理初心者が「GPT2-japanese」で遊んでみた (2022-07-08)
  2. Tauri でデスクトップアプリ開発を始める (2022-07-08)
  3. Deno による Slack プラットフォーム(オープンベータ) (2022-09-27)
  4. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  5. ORマッパーのTypeORMをTypeScriptで使う (2022-07-27)
  6. 第1回 OpenAPI Generator を使ったコード生成 (2022-06-04)
  7. 直感が理性に大反抗!「モンティ・ホール問題」 (2022-07-04)
  8. Rust によるデスクトップアプリケーションフレームワーク Tauri (2022-03-06)
  9. 箱ひげ図で外れ値を確認する (2022-05-18)
  10. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)