メインコンテンツまでスキップ

WebAPI 実装ガイド

本カタログで WebAPI を実装するプラクティスを記載したドキュメントです。ポイントは以下2点です。

  • WebAPI の主要機能は AWS 公式ライブラリ Powertools for AWS Lambda (Python) を利用
  • 内部アーキテクチャとして Lambdalith 構成を採用
    • API ごとに Lambda リソースを分離するより、パフォーマンスとと運用便宜性に優れる
    • 従来の WebAPI フレームワークの開発構成を踏襲できる

ユーザー情報を登録する API の実装

ユーザー情報の入力を受け、ユーザーデータを新規登録する API を実装します。

API 仕様

以下のような仕様のサマリーを作成します。

  • 正常系

    [Request sample]
    POST /users
    {
    "name": "Shigeo",
    "country": "Japan",
    "age": 30
    }

    [Response sample 1]
    200 OK
    {
    "name": "Shigeo",
    "country": "Japan",
    "age": 30
    }
  • 異常系(name未指定)

    [Request sample]
    POST /users
    {
    # "name": "Shigeo",
    "country": "Japan",
    "age": 30
    }

    [Response sample]
    400 Bad Request
    {
    "message": "Missing name. name is required query parameter."
    }

大まかな処理の流れ

Lambdalith の推奨構成として、大きく以下4つのレイヤーで構成され、上から順に呼ばれていきます。

レイヤー名サマリー
API Routingマッピングされた Action を呼び出す
Action入力チェック、Service の呼び出しを行う
ServiceAPI のメインロジック・ビジネスロジック
Daoデータベースアクセス処理・クエリー実行

他にも、AWS SDK 実装を分離した aws/、外部ライブラリを分離した lib/、データのモデリングやスキーマを定義する models/ など、必要に応じて適宜ディレクトリを構成してください。

API Routing 実装

リクエストを受け付けると、Lambda 関数のエントリーポイント( src/sample_api/index.ts )にリクエスト情報が入った event オブジェクトが渡されます。Powertools から提供されるデコレータを利用して以下のように適切な Action に振り分けます。

実装例

  • src/sample_api/index.ts
from aws_lambda_powertools.event_handler import APIGatewayHttpResolver
from aws_lambda_powertools.event_handler.exceptions import BadRequestError

# app をインスタンス化
app = APIGatewayHttpResolver()

# Lambda 関数のエントリーポイント
def handler(event, context):
return app.resolve(event, context)

# POST /users をこの関数にマッピング
@app.post("/users")
def post_users():
event = app.current_event
return CreateUsersAction().handle(http_api_event=event)

# 処理の中で BadRequestError が投げられたら、こちらの関数が呼ばれる
@app.exception_handler(BadRequestError)
def handle_400_bad_request_error(exception):
return Response(status_code=400, content_type="application/json", body=json.dumps({"message": exception.msg}))

# 処理の中で意図しない Exception が投げられたら、こちらの関数が呼ばれる
@app.exception_handler(Exception)
def handle_500_internal_server_error(e: Exception):
# こちらのサンプルコードはカタログ AMI に含まれています。

Action 実装

「Action」は、以下の役割を担当します。

  • API 処理の「入り口」と「出口」となる部分
  • 入力チェック、Service 実行、レスポンスデータ型の整理等を行う
  • Controller 層と言われることもある

実装例

  • src/sample_api/actions/create_users_action.py
import json
from aws_lambda_powertools.event_handler.exceptions import BadRequestError
from sample_api.services.users_service import UsersService

class CreateUsersAction:

def handle(self, http_api_event):
# リクエストボディを取り出す
req_body = json.loads(http_api_event.body)

# リクエストボディの項目を取り出す
name = req_body.get('name')
country = req_body.get('country')
age = req_body.get('age')

# 入力チェックを行う
self.validate_required_params(name)
# 他にも、age が numeric 型であるか、そもそも body が None になっていないかの確認が必要

# Service のインスタンス化と実行
service = UsersService()
service.create_users(name, country, age)

# レスポンス情報の組み立てと返却
return {
'statusCode': 200,
'body': {
'message': "Successfully created user data."
}
}

def validate_required_params(self, name):
if name is None:
raise BadRequestError('Missing name. name is required query parameter.')

def validate_body_is_not_empty(self, http_api_event):
# こちらのサンプルコードはカタログ AMI に含まれています。

def validate_numeric_params(self, age):
# こちらのサンプルコードはカタログ AMI に含まれています。

def is_numeric(self, val):
# こちらのサンプルコードはカタログ AMI に含まれています。

Service 実装

「Service」は、API のメインロジック、ビジネスロジックを担当する部分です。

実装例

  • src/sample_api/services/users_service.py
from aws_lambda_powertools import Logger
from sample_api.daos.users_dao import UsersDao

users_dao = UsersDao()

class UsersService:

def create_users(self, name, country, age):
# ユーザー作成のための処理を実装します
# 例えば、ユニークな ID 採番ルールがある場合は、採番処理を実装します
# 登録準備が整ったら、DAO を経由してデータベースにユーザー情報の書き込み処理を実施します
new_users_record = users_dao.create_users(name, country, age)

return new_users_record

DAO 実装

「DAO」は、データベースにアクセスし、クエリーを実行する処理を担当します。

実装例

  • src/sample_api/actions/users_service.py
import os, psycopg
from psycopg.rows import dict_row


class UsersDao:
def get_connection_info(self):
# psycopg3 のコネクション作成に必要なパラメータ情報を読み込みます。
# こちらのサンプルコードはカタログ AMI に含まれています。

def create_user(self, name, country, age):
try:
# データベースのコネクションを作成します
# 実際には、コネクションプールの仕組みを検討してください。
conn_info = self.get_connection_info()
conn = psycopg.connect(**conn_info)
with conn.cursor() as cur:
cur.execute(
"INSERT INTO users (name, country, age) VALUES (%s, %s, %s)",
(name, country, age)
)
conn.commit()
conn.close()

動作確認

ローカル実行によるデバッグとユニットテスト」を参照してください。