1 Lambda 関数の 関数 URL でどこまで戦えるか。
Motivation
エンタープライズな環境に身をおいていると、どうしても管理画面が必要になるときがあります(個人の感想です)。 でもひとたびこれを SPA にしてー、Cloudfront+S3でサーブしてー みたいなことをすると
- S3 バケットの改ざん検知をしなさい
- IP アドレス制限で社内アクセスに限定しなさい
みたいな追加課題が降ってきます。特に後者は WAF でやるくらいしか手がないので、ランニングコストにも少なからず影響します。
というわけで、1つの Lambda の関数 URL だけで Web アプリが成立するのであれば、使える局面があるのではないかと考えました。
前提条件
- Backend の API サーバと、Frontend の SPA を 1 Lambda で捌く
- モジュールスコープなキャッシュの効率のため Reserved Concurrency は1
- CSS フレームワークあるいは UI コンポーネントは使いたい
- JS, CSS などを外部のリソースには頼らない
構成
app/backend , app/frontend の成果物を app/package.json の script で Lambda デプロイ用の zip でまとめる感じ。
Hono が API ルーティングと SPA の静的ファイル配信の両方を担当する。Lambda のハンドラも Hono の AWS Lambda アダプタで処理。
lambda-fullstack-app/
├── app/
│ ├── backend/ # Hono (API + 静的ファイル配信)
│ │ └── src/
│ │ └── index.ts
│ ├── frontend/ # Preact SPA
│ │ ├── src/
│ │ │ ├── main.tsx
│ │ │ └── app.tsx
│ │ ├── index.html
│ │ └── vite.config.ts
│ ├── package.json # pnpm workspaces ルート + deploy スクリプト
│ └── pnpm-workspace.yaml
├── infra/ # OpenTofu (Lambda + 関数 URL + IAM)
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── providers.tf
│ └── versions.tf
└── devbox.json # 開発環境 (Node.js + pnpm + OpenTofu)
工夫
vite-plugin-singlefileで JS, CSS を HTML にインライン化し、SPA の初回ロードを1リクエストで完結させることで 429 にあたる率を下げる
良かった点
- まぁ動く
- 接続元 IP アドレス制限をしたい場合は、コードで実装する必要はあるものの、Lambda 以外のリソースに頼らずやれる
- 429 で困ったら、Reserved Concurrency を上げればどうにでもなる
- 身内がたまに使う程度であれば許されるのでは
制約
兎にも角にも、429 との戦い。Reserved Concurrency 増やせばいいことではある。
- Backend API から複数リソースの同時取得
- 複数人が同時に使う