一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • AWS Lambda + API Gatewayで作るサーバーレスAPIの設計と運用

サーバーレスAPIの全体像

AWS LambdaとAPI Gatewayの組み合わせは、サーバーレスアーキテクチャにおけるAPI構築の定番パターンです。インフラ管理が不要で、リクエスト数に応じた従量課金という特性から、スタートアップから大企業まで幅広く採用されています。

しかし、実際に本番運用してみると「コールドスタートが遅い」「デバッグが難しい」「コスト管理が予想以上に大変」といった課題に直面します。筆者は過去3年間で複数のサーバーレスAPIを本番運用してきた経験をもとに、設計段階で押さえるべきポイントと運用ノウハウを解説します。

Lambda関数の設計パターン

Lambda関数の粒度は、プロジェクト規模とチーム構成に応じて選択します。代表的なパターンを紹介します。

単一関数パターン(モノリシックLambda)

1つのLambda関数で全てのAPIエンドポイントを処理するパターンです。小規模なAPIやプロトタイプに向いています。

// handler.ts - 単一関数で全ルートを処理
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'

export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
const { httpMethod, path } = event

// ルーティング
if (path === '/users' && httpMethod === 'GET') {
return await listUsers(event)
}
if (path.match(/^\/users\/[^/]+$/) && httpMethod === 'GET') {
return await getUser(event)
}
if (path === '/users' && httpMethod === 'POST') {
return await createUser(event)
}

return {
statusCode: 404,
body: JSON.stringify({ message: 'Not Found' }),
}
}

async function listUsers(event: APIGatewayProxyEvent) {
// DynamoDB等からユーザー一覧を取得
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ users: [] }),
}
}

機能別関数パターン

エンドポイントごと、またはリソースごとにLambda関数を分割するパターンです。中〜大規模なAPIに推奨されます。関数ごとにメモリやタイムアウトを最適化でき、デプロイの影響範囲も限定できます。

API Gatewayの構成とIaCによる管理

インフラはコードで管理すべきです。AWS SAM(Serverless Application Model)を使った構成例を示します。

# template.yaml - AWS SAMテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
Function:
Runtime: nodejs20.x
Timeout: 30
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref UsersTable
STAGE: !Ref Stage

Parameters:
Stage:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- prod

Resources:
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Cors:
AllowOrigin: "'*'"
AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization'"

ListUsersFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/users.list
Events:
Api:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /users
Method: GET

CreateUserFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/users.create
Events:
Api:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /users
Method: POST

UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub '${Stage}-users'
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH

コールドスタート対策

コールドスタートはサーバーレスAPIの最大の課題です。初回リクエストや一定時間アクセスがなかった後のリクエストで、Lambda実行環境の初期化に時間がかかります。

対策の選択肢

  • Provisioned Concurrency:事前にウォーム状態のインスタンスを確保します。最も確実だがコストがかかる
  • SnapStart(Java向け):初期化済みのスナップショットから起動することで高速化します
  • 依存関係の最小化:不要なライブラリを削除し、バンドルサイズを小さくします
  • Lambda Layers:共通ライブラリをLayerに切り出し、デプロイパッケージを軽量化します

# Provisioned Concurrencyの設定(SAMテンプレート)
ListUsersFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/users.list
AutoPublishAlias: live
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 5

筆者の経験では、P99レイテンシが1秒以下を求められるAPIにはProvisioned Concurrencyを設定し、それ以外は依存関係の最適化で対応するのがコストパフォーマンスに優れたアプローチです。

ログ・監視・エラーハンドリング

サーバーレス環境では、構造化ログとCloudWatchの活用が運用の要です。

// 構造化ログの実装例
const logger = {
info: (message: string, context: Record<string, unknown> = {}) => {
console.log(JSON.stringify({
level: 'INFO',
message,
timestamp: new Date().toISOString(),
requestId: context.requestId,
...context,
}))
},
error: (message: string, error: Error, context: Record<string, unknown> = {}) => {
console.error(JSON.stringify({
level: 'ERROR',
message,
errorName: error.name,
errorMessage: error.message,
stackTrace: error.stack,
timestamp: new Date().toISOString(),
...context,
}))
},
}

CloudWatch Logs Insightsを使えば、構造化ログに対して強力なクエリを実行できます。エラー率の監視やレイテンシの分析が容易になり、障害対応の速度が格段に上がります。

まとめ:サーバーレスは銀の弾丸ではないです

Lambda + API Gatewayの組み合わせは強力ですが、全てのユースケースに最適とは限りません。長時間実行が必要な処理、WebSocket接続が中心のアプリケーション、予測可能な高トラフィックを処理するAPIでは、ECSやEC2の方がコスト効率が良い場合もあります。サーバーレスの特性を正しく理解し、プロジェクトの要件に合致するか慎重に判断してから採用しましょう。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア