Django で AWS Lambda のコードをそのまま実行しよう

はじめに
はじめまして、入社2年目、来日4年目の者です。
チームのプロジェクトで AWS API Gateway をトリガーにして、
Lambda 関数を実行するような構成を採用していますが、
開発を進めるにつれて、次第に Lambda 関数の影響範囲が広がり、
Lambda 関数を改修すると、チームのほかのメンバーがシステムを利用できなくなる、という課題が浮上していました。
さすがに一人ひとりに個別の AWS 開発環境を用意するのは難しいため、
ローカルでも Lambda 関数を実行できる方法が必要になります。
LocalStack、AWS SAMなど公式でも非公式でもこういう課題向けのサービスがありますが、
こちら側の事情で、自作寄りの手法を選んでいました。
サンプルとなる Lambda 関数
例えば、以下のようにランタイムを Python とし、API を呼び出した際の情報をそのまま返却する、シンプルな Lambda 関数があります。
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps(event)
}

この Lambda 関数を $APIGATEWAY_URL/a_simple_api で呼ぶことを可能にします。
API を呼ぶと
図のようなものが返却されます。
Django プロジェクトを立ち上げよう
今回のサンプルとなる Lambda 関数は Python で書いているため、
Django を選びました。
(Python の Web アプリケーションフレームワークには他にも選択肢がありますが、特に強い理由はなく、今回は Django を採用しました。)
- python の仮想環境を作る (推奨、省略可)
(筆者のパソコンのグローバル環境では Python を実行する際のコマンドは 「python3」なので、仮想環境を作る時 python3 で実行しています。仮想環境に入ると 「python」にします)
$python3 -m venv .venv$source .venv/bin/activate
- Django をインストール
$python -m pip install Django
- Django プロジェクトを作って、アプリも作成
$mkdir django_project$django-admin startproject django_project django_project$cd django_project$python manage.py startapp lambda_local作成が完了したら、全体構成は以下の図のように

Django プロジェクトに Lambda 関数のスクリプトを入れる
- lambda_local ディレクトリに a_simple_function.py を作成し、Lambda 関数のコードを実装します。

- レスポンスを処理するため、djangorestframework をインストール
$ python -m pip install djangorestframework
- lambda_local/views.py を以下のように編集する
import importlib import json from rest_framework.response import Response from rest_framework.decorators import api_view from django.conf import settings from datetime import datetime from pathlib import Path # 簡易 context クラス class LocalContext: function_name = "local-lambda" aws_request_id = "local-" + datetime.utcnow().isoformat() def invoke_lambda(dotted_path: str, event: dict): mod_name, func_name = dotted_path.rsplit(".", 1) module = importlib.import_module(mod_name) func = getattr(module, func_name) context = LocalContext() return func(event, context) def build_event(request): """API Gateway HTTP API v2 の event に近い形で生成""" event = { "version": "2.0", "routeKey": f"{request.method} {request.path}", "rawPath": request.path, "rawQueryString": request.META.get("QUERY_STRING", ""), "queryStringParameters": request.GET.dict() or None, "headers": {k[5:].replace('_', '-').lower(): v for k,v in request.META.items() if k.startswith("HTTP_")}, "requestContext": { "http": { "method": request.method, "path": request.path, "sourceIp": request.META.get("REMOTE_ADDR") } }, "body": request.body.decode() if request.body else None, "isBase64Encoded": False, } return event # --- URLごとにどの Lambda を呼ぶかを書く ----------------------- @api_view(['GET']) def a_simple_api(request): event = build_event(request) result = invoke_lambda("lambda_local.a_simple_function.lambda_handler", event) return Response( data=json.loads(result["body"]), status=result["statusCode"], headers=result.get("headers", {}) )
- lambda_local/ に urls.py を作成、urlpatterns を記入する
from django.urls import path from . import views urlpatterns = [ path('a_simple_api', views.a_simple_api), ]
- django_project/urls.py を編集して、urlpatterns に local_lambda/ のパスを入れる
from django.urls import path, include urlpatterns = [ path('lambda_local/', include('lambda_local.urls')), ]
サーバーを実行しよう
ターミナルに以下のコマンドを入力、実行する
$ python manage.py runserver

これにより、先ほど定義した API のパスを呼ぶと、
Amazon API Gateway と同様の形式のレスポンスが返されます。
最後に
上記の手順を踏まえたら、Lambda 関数のスクリプトを変更しなくてもローカルで実行できます。
もし Lambda の Layers を使っているなら、通常の Python の自作ライブラリをインポートする感覚で問題ありません。
API Gateway の Authorization 機能を利用している場合は、build_event 内で event を生成する前に、独自の認証ロジックを組み込んでください。
(Cognito のトークンで認証している場合は、以下に Python 用の検証ロジックがあります)
https://github.com/awslabs/aws-support-tools/blob/master/Cognito/decode-verify-jwt/decode-verify-jwt.py








