こんにちは、クラウド事業部の髙野です。
今回はAWSでサーバーレスアーキテクチャを採用したREST APIの実装方法を紹介します。
「サーバーレスアーキテクチャについて、なんとなくの概要は知っているけど実装できるかは不安だな」という方の参考になればと思います。
目次
サーバーレスアーキテクチャとは
サーバーレスアーキテクチャ(以下:サーバーレス)とは、サーバーを自身で管理せずにアプリケーションの構築と実行を実現するアーキテクチャのことを指します。
サーバーレスには主に以下のようなメリット、デメリットがあります。
メリット
- サーバーを管理、運用する費用を抑えられる
- コンピューティングリソースの拡張が容易
デメリット
- 即応性が低い
サーバーレスを採用することでサーバーの運用に関するリソースを削減できるため、開発者はサービスの開発に時間とエネルギーを費やすことができるようになります。しかし即応性が低いため、実行頻度が多く、低レイテンシーなレスポンスを求められるサービスには適さないです。
作成するAPIの概要
今回作成したAPIはクライアントPCからHTTPリクエスト(POST/GET)で商品情報を送信すると、メソッドに応じて、「DBに商品情報を登録」「登録済み商品情報の取得」を行うだけのシンプルなものです。
使用するAWSサービスについて
DynamoDB
DynamoDBはサーバーレスなkey-value型NoSQLデータベースを提供するサービスです。
NoSQLデータベースはリレーショナルデータベースのようにカラムの型などを事前に定義することなく利用可能です。
Lambda
Lambdaは、サーバーレスでイベント駆動型のコンピューティングリソースを提供するサービスです。
ユーザーはサーバーのプロビジョニングや管理をすることなく、あらゆるタイプのアプリやバックエンドサービスのプログラムを実行できます。
Lambdaで作成したプログラムはLambda関数と呼ばれ、今回作成したアプリでは後述するAPI GatewayによってこのLambda関数が呼び出され、DynamoDB上でデータを操作するプログラムが実行されます。
API Gateway
API GatewayはAPIの作成や管理を行うためのサービスです。
API Gatewayを使用すると、Lambdaで実行しているコードのカスタムAPIをすばやく簡単に作成し、LambdaコードをAPIから簡単に呼び出せます。
IAM
IAMはAWS上のユーザーやリソースの認証を管理するためのサービスです。
今回作成したアプリでは、Lambda関数がDynamoDBのテーブルにアクセスするための権限を付与するのに使用しています。
環境構成
No. | 処理内容 | 備考 |
---|---|---|
1 | クライアントPCからAPI Gatewayに向けてHTTPリクエスト(POST/GET)を送信する | |
2 | API GatewayはHTTPリクエストを受信すると各メソッドに応じたLambda関数を起動させる | |
3 | Lambda関数内でDynamoDB上のテーブルに対する処理(登録/取得)を行う | |
4 | DynamoDB上のテーブルから取得したデータをAPI Gateway経由でクライアントPCに返す | メソッドがGETの場合は本処理も実行 |
API作成手順
1. DB作成
商品情報を登録するためのitemテーブルをDynamoDBに作成します。
AWSコンソールにログインして、DynamoDBのコンソール画面を開きます。
サイトバーの「テーブル」をクリック → 「テーブルの作成」をクリックします。
設定画面で以下を指定し、その他はデフォルトのままで「テーブルの作成」をクリックします。
- テーブル名 → items
- パーティションキー → id
テーブルの作成完了まで数分かかるので、完了まで待機します。
テーブルの作成が完了し、itemsテーブルの状態がアクティブになっていることを確認します。
2. IAMロール作成
次の手順で作成するLambda関数がitemsテーブルにアクセスするための権限を付与するのに使用するIAMロールを作成します。
IAMのコンソール画面を開きます。
サイドバーの「ロール」をクリック → 「ロールを作成」をクリックします。
設定画面で以下を指定し、「次へ」をクリックします。
- 信頼されたエンティティタイプ → AWSのサービス
- サービスまたはユースケース → Lambda
検索ボックスで以下を入力し、チェックボックスにチェックを入れます。
2つともチェックを入れたら「次へ」をクリックします。
- AWSLambdaBasicExecutionRole
- AmazonDynamoDBFullAccess
設定画面で以下を指定し、「ロールを作成」をクリックします。
- ロール名 → items-role
3. Lambda関数作成
作成したitemsテーブルに商品情報を登録および取得するためのLambda関数を以下の名称で作成します。
- 商品情報の登録用 → items-post-function
- 商品情報の取得用 → items-get-function
まずは登録用のLambda関数を作成します。
Lambdaのコンソール画面を開きます。
サイドバーの「関数」をクリック → 「関数の作成」をクリックします。
設定画面で以下を指定し、その他はデフォルトのままで「関数の作成」をクリックします。
- 関数名 → items-post-function
- ランタイム → Python3.9
- 実行ロール → 既存のロールを使用する
- 既存のロール → items-role
作成されたLambda関数にはデフォルトでサンプルコードが設定されているので、このコードを実際に使用するものに修正します。
作成したitems-post-functionの詳細画面を開き、「コード」タブをクリックします。
エディター部分にサンプルコードが記述されているので、これを削除して以下のコードを貼り付けます。
import boto3 import json dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('items') def post_items(requestJSON): table.put_item(Item={'id': requestJSON['id'], 'name': requestJSON['name'], 'price': requestJSON['price'], 'stock': requestJSON['stock']}) def lambda_handler(event, context): requestJSON = json.loads(event['body']) post_items(requestJSON)
貼り付けが完了したら、エディター上部の「Deploy」をクリックします。
同じ要領でデータ取得用のLambda関数も作成します。
関数の作成画面で以下を指定し、その他はデフォルトのままで「関数の作成」をクリックします。
- 関数名 → items-get-function
- ランタイム → Python3.9
- 実行ロール → 既存のロールを使用する
- 既存のロール → items-role
デフォルトのサンプルコードを実際に使用するコードに修正します。
作成したitems-get-functionの詳細画面を開き、「コード」タブをクリックします。
エディター部分にサンプルコードが記述されているので、これを削除して以下のコードを貼り付けます。
import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('items') def get_item(id): response = table.get_item( Key={ 'id': id } ) return response['Item'] def lambda_handler(event, context): return get_item(event['id'])
貼り付けが完了したら、エディター上部の「Deploy」をクリックします。
4. API作成
ここからはLambda関数を呼び出すためのAPIをAPIGatewayを使用して作成します。
APIGatewayのコンソール画面を開きます。
サイドバーの「API」をクリック → 「APIを作成」をクリックします。
REST APIの「構築」をクリックします。
設定画面で以下を指定し、その他はデフォルトのままで「APIを作成」をクリックします。
- API名→items-api
5. API作成(リソース)
次にリソースを作成します。
作成したAPI(items-api)のリソース画面で「リソースを作成」をクリックします。
リソース名にitemsと入力して、「リソースを作成」をクリックします。
APIのリソース画面で「/items」が追加されていることを確認します。
6. API作成(POSTメソッド)
作成した「/items」リソースにPOSTメソッドを定義します。
「/items」リソースをクリック → 「メソッドを作成」をクリックします。
設定画面で以下を指定し、その他はデフォルトのままで「メソッドを作成」をクリックします。
- メソッドタイプ→POST
- Lambda関数 → 「items-post-functionを作成したリージョン」「items-post-function」
次にPOSTメソッドに対してマッピングテンプレートの設定を行います。
※マッピングテンプレート → メソッドパラメーターとして渡された値を後続のLambda関数で利用可能な形に整形するためのスクリプト。
「POST」をクリック → 「統合リクエスト」タブをクリックします。
画面下部にスクロール → マッピングテンプレートの「テンプレートを作成」をクリックします。
コンテンツタイプにapplication/jsonと入力し、テンプレート本文に下記を貼り付けて「テンプレートを作成」をクリックします。
{ "body" : "$util.escapeJavaScript($input.body)" }
これでPOSTメソッドに関する設定は完了したのでデプロイを行います。
「APIをデプロイ」をクリックします。
設定画面で以下を指定し、「デプロイ」をクリックします。
- ステージ → New stage
- ステージ名 → items-stage
※ステージ → APIをデプロイする環境のことで、実務では開発用を「dev」、本番用を「prod」などとして、デプロイ用の環境を分ける等の目的で使用する。
7. API作成(GETメソッド)
POSTメソッドに続いて、GETメソッドを定義します。
「/items」リソースをクリック → 「メソッドを作成」をクリックします。
設定画面で以下を指定し、その他はデフォルトのままで「メソッドを作成」をクリックします。
- メソッドタイプ → GET
- Lambda関数 → 「items-get-functionを作成したリージョン」「items-get-function」
次にGETメソッドに対してマッピングテンプレートの設定を行います。
「GET」をクリック → 「統合リクエスト」タブをクリックします。
画面下部にスクロール → マッピングテンプレートの「テンプレートを作成」をクリックします。
コンテンツタイプにapplication/jsonと入力し、テンプレート本文に下記を貼り付けて「テンプレートを作成」をクリックします。
{ "id" : "$input.params('id')" }
これでGETメソッドに関する設定は完了したのでデプロイを行います。
「APIをデプロイ」をクリックします。
ステージ名にitems-stageを選択し、「デプロイ」をクリックします。
8. 動作確認
最後にcurlコマンドを使用して、デプロイしたAPIを呼び出し、動作確認を行います。
まずは呼び出し用のURLを確認します。
items-stageの詳細画面の「URLを呼び出す」に表示されているURLを控えておきます。
curlコマンドを実行できる環境にて以下のコマンドを実行します。
curl -X POST -H "Content-Type: application/json" -d "{\"id\":\"001\",\"name\":\"Apple\",\"price\":\"100\",\"stock\":\"10\"}" <控えておいたURL>/items
問題なく処理が完了すれば、POSTはデータの登録のみで戻り値がないためnullと表示されます。
次に先ほどPOSTで登録したデータをGETを使って取得してみます。
curl -X GET <控えておいたURL>/items?id=001
APIが問題なく動作すれば、登録した商品情報が以下のように返ってきます。
{"stock": "10", "id": "001", "price": "100", "name": "Apple"}
おわりに
今回はじめてサーバーレスなREST APIを自身で構築して、今まであまり触れたことのないサービスに触れることができました。とくにAPIGateway、Lambdaはかなり登場回数の多い組み合わせだと思うので経験できてよかったです。実務でサーバーレスを採用することになった際には今回の経験を活かしてスムーズに開発を行っていきたいです。
お知らせ
私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。
https://www.ap-com.co.jp/service/utilize-aws/
また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。