こんにちは。
ソーイ株式会社1年目、村上です。
皆さん、Laravel使ってますか?
初めてこのフレームワークを触ったとき、私は「魔法」のような利便性に感動しました。
ルートファイルに1行書けばURLが決まり、コントローラを書けばリクエストデータが手に入り、モデルを介せばDB操作も自由自在。
あまりにスムーズすぎて、時々その裏側がブラックボックスに感じることもあります。
「もし、このLaravelが当たり前にやってくれていることを、AWSの機能だけで再現したらどうなるんだろう?」
そんな純粋な好奇心から、API GatewayやLambdaを使って「Laravelのあの挙動」を分解・構築してみた記録がこの記事です。今回は第1回という感じですね。
この記事でわかること
- Laravelの主要機能(Route/Controller/Model)に対応するAWSサービスの役割
- API GatewayとLambdaを組み合わせた、サーバーレスなAPI構築の具体手順
- DynamoDBへのデータ保存方法
- CloudShellでの動作確認と、CloudWatch Logsを用いたデバッグ方法
全体構成
今回は、Laravelでの開発フローになぞらえて、以下の2つのフェーズで進めていきます。
1. Hello World編(ルーティングとロジックの分離)
Laravelの「ルート定義」と「コントローラ」の関係を、Amazon API GatewayとAWS Lambdaで再現します。リクエストを受け取り、レスポンスを返すというAPIの基本構造を学びます。
使用するサービス
AWS Lambda(Controllerの代わり)
サーバーレスでコードを実行できる関数実行サービス
Amazon API Gateway(Routeの代わり)
外部からのリクエストを受け取り、適切な処理(Lambdaなど)へ振り分けるサービス
全体図
2. データ保存編(永続化の仕組み)
Laravelの「Eloquent(モデル)」によるDB保存を、Amazon DynamoDBを使って再現します。サーバーレス環境でDB接続と、権限管理(IAM)の付与を行います。
使用するサービス
AWS Lambda(Controllerの代わり)
サーバーレスでコードを実行できる関数実行サービス
Amazon API Gateway(Routeの代わり)
外部からのリクエストを受け取り、適切な処理(Lambdaなど)へ振り分けるサービス
Amazon DynamoDB(databaseの代わり)
高速にデータを保存・取得できるサーバーレスNoSQLデータベースサービス
全体図
1. Hello World編(ルーティングとロジックの分離)
まずは、ブラウザやツールからURLを叩くと{"message": "Hello World"}というJSONが返ってくるだけの、シンプルなAPIを作ります。
手順1ロジックを担当するLambda関数の作成
Laravelでいうところの「コントローラのメソッド」を先に作ります。
AWSコンソールで「Lambda」を検索し、[関数の作成] をクリックします。
[一から作成] を選択し、関数名 と ランタイム を選択し、[関数の作成] をクリックします。
コードエディタ(index.mjs)に以下のコードを貼り付けて [Deploy] します。
export const handler = async (event) => {
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: "Hello World from Lambda!" }),
};
};
手順2 ルーティングを担当するAPI Gatewayの作成
次に、外部からのリクエストを受け付ける ルート を定義します。
API Gateway のコンソールを開き、 APIを作成 をクリック。
HTTP APIの 構築 を選択します。

API Gatewayには REST API というそのものな選択肢もあります。しかし、今回は「Laravelのルーティングをシンプルに再現する」ことが目的のため、より軽量で設定が簡単な HTTP API を選択しました。
[統合を追加] で Lambda を選び、先ほど作ったLambdaの関数名を指定します。
その後、API名を入力し次へ。

[ルートを設定] で今回は以下のように設定します。
メソッド: GET
リソースパス: /hello
そのまま [次へ] を押し続け、最後に [作成] をクリックします。
設定完了後は作成したAPIの詳細ページへ飛び、 デフォルトのエンドポイントを確認。そのURLの末尾に先ほど設定したリソースパスを付けた状態でページに訪問すると...?
URLの形式は
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/helloのような形になります。
見事表示できました。
Laravelとの比較:何が起きたのか?
無事に「Hello World」が表示されたところで、今行った作業をLaravelの知識で整理してみましょう。
| 役割 | Laravelでの実装 | AWSでのサービス |
|---|---|---|
| URL(入口)の定義 |
Route::get('/hello', ...) を書く |
API Gateway の「ルート」設定 |
| 実行する処理(中身) |
Controller のメソッド |
Lambda の handler 関数 |
| 入口と処理の紐付け | [Controller::class, 'index'] |
API Gateway の 「統合 (Integration)」 設定 |
Laravelではプロジェクトという「一つの箱」の中にルートとコントローラが同居していますが、AWSでは 「窓口(API Gateway)」 と 「作業員(Lambda)」 が物理的に独立したサービスとして分かれています。
API Gatewayがリクエストを受け取り、それをLambdaにパスしてレスポンスを作ってもらう。この 疎結合(バラバラな状態) こそが、サーバーレスアーキテクチャの基本形とわかりますね。
2. データ保存編(永続化の仕組み)
次は、API経由で受け取ったデータをDBに保存する処理を作ります。
Laravelなら Migration を作ってテーブルを作成し、 Model::create() を叩く場面ですが、AWSでは 「テーブル作成」「権限付与」「保存コードの記述」 の3ステップが必要です。
手順1 DynamoDB でテーブルを作成する
DynamoDB コンソールで [テーブルの作成] をクリック。

テーブル名: TTT(名前はなんでもいいです。)
パーティションキー: id (文字列)
他はデフォルトのまま [テーブルの作成] を押下。
手順2 IAMでLambdaにDB操作の許可を与える
Laravelでは .env にDBのパスワードを書きますが、AWSでは 「このLambdaは、このDynamoDBを触っていいよ」 という許可となる権限を渡す必要があります。
第一章で作成したLambdaの設定タブを開きます。
[設定] > [アクセス権限] にある「ロール名」のリンクをクリック。
IAMの画面が開くので、[許可を追加] > [ポリシーをアタッチ] をクリック。

AmazonDynamoDBFullAccess を検索してチェックを入れ、[許可を追加] をクリックします。
※本来は最小権限が望ましいですが、今回は実験のためフルアクセスを使います。
手順3 保存処理の実装(Controllerの更新)
Lambdaのコードを以下のように書き換えて [Deploy] を押下します。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({});
const doc = DynamoDBDocumentClient.from(client);
export const handler = async (event) => {
// Laravelの $request->json()->all() に相当
const body = JSON.parse(event.body || "{}");
const id = body.id || Date.now().toString();
const command = new PutCommand({
TableName: "TTT",
Item: {
id: id,
task: body.task || "名無しのタスク",
createdAt: new Date().toISOString()
}
});
await doc.send(command);
return {
statusCode: 201,
body: JSON.stringify({ message: "保存成功!", id: id }),
};
};
動作確認
実装とデプロイが完了したら、実際にデータを保存できるかテストしてみましょう。
作成したAPIを編集し、GETからPOSTに変更しましょう。
その後、AWS CloudShellからcurlコマンドを使用し動作確認を行います。
CloudShellとは、ブラウザ上で即座に利用できるAWS専用ターミナル環境です。
ログイン中のIAM権限が自動で適用されるため、認証設定を追加することなくAWS CLIによるリソース操作をすぐに実行できます。
詳しい話はドキュメントを確認してください。
REST APIで設定していればテストを行える項目がありますが、今回はHTTP APIで設定したためありません。
curlコマンドで使用するAPIのURLをゲットします。
API Gateway左タブにあるStagesからAPIで使っているステージを押下し、右に表示されるURLを呼び出すにあるものをコピーしてください。
形式:https://[APIの固有ID].execute-api.[リージョン名].amazonaws.com
次にAWS CloudShellを開きます。
AWSコンソールの一番上にある検索窓の右側、[>_] のようなアイコンをクリックします。

起動したらそこでcurlコマンドを実行します。
curl -X POST https://あなたのAPIのURL/hello \
-H "Content-Type: application/json" \
-d '{"id": "task-001", "task": "CloudShellからテスト"}'
実行後、成功したら下記のレスポンスが現れます。
{"message":"保存成功!","id":"task-001"}
この状態でDynamoDBを確認し、入力したデータが保存されているか確認しましょう。
DynamoDB > 項目を探索 > テーブル名 > テーブルアイテムの探索 > 返された項目
このページから確認できます。

動作確認でエラーした場合
もし、成功せずエラーした場合は、Lambdaのモニタリングからログを確認しましょう。
わざとDynamoDBの名前を変更して実践してみます。
まずターミナルでは以下の表示が出ます。
{"message":"Internal Server Error"}
と出ました。
この文からだとエラーしたという情報しかわからないため、Lambda関数のモニタリングから確認しましょう。
場所は、自分の関数詳細ページ > モニタリング > CloudWatch Logs > Failed invocationsにエラーした場合その原因が書かれます。
今回は以下の内容のログが入っていました。
"errorType":"ResourceNotFoundException"
これであれば指定したリソースが見つかりませんと意味がよくわかるため、修正も可能です。
Laravelとの比較:DB接続と保存の考え方
DynamoDBへの保存を実装したところで、Laravelの「Eloquent」での操作と比較してみます。
| 役割 | Laravel (Eloquent) | AWS (Serverless) |
|---|---|---|
| テーブル定義 | Migrationファイル + migrate
|
DynamoDB コンソールで作成 |
| セキュリティ | .envにユーザー名とパスワードを記述し、特定のユーザーとしてDBにログインする | IAMロール でLambdaに権限を付与 |
| 保存操作 | Task::create([...]) |
AWS SDK の PutCommand
|
| データ形式 | RDB(テーブル形式) | NoSQL(JSON形式 / ドキュメント型) |
Laravelでは .env にDBのユーザー名とパスワードを記述し認証としますが、AWSでは「IDやパスワード」をコードに持たせるのではなく、「どのサービスが、何をしていいか」という許可証(IAMロール) を直接サービスに持たせることでセキュリティを担保します。
「コードに秘密情報を書かなくていい」というのは気の持ちようとしては楽ですね。
まとめ
今回は、普段 Laravel が「魔法」のように解決している機能を AWS で分解・再構築してみました。
ブラックボックスになりがちなフレームワークで行っている処理を、疎結合という特徴を持ったマネージドサービスで再現することで、普段どのような部分が自動的に処理が行われているのか仕組みをメタ解析するアプローチとして勉強になりました。
今後も「当たり前」を疑い、技術の裏側を解剖するような学習を続けていきます。
公式ドキュメント参考ページ
お知らせ
技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii








