内容
DynamoDBのデータを取ってくるAPIを、AWS SAMで構築してみる
要するに、こんな感じのテーブルから、、、
group(Hash) | name(Range) |
---|---|
group1 | name1 |
group1 | name2 |
group2 | name3 |
こんな感じに取ってこれるようにしたい。
curl https://hogehoge/Prod/dynamo-api/v1/?group=group1&name=name1
{
"result": [
{
"name": "group1",
"group": "name1"
}
]
}
環境
- macOS Mojave -10.14
- python3.7.4
- sam 0.38.0
目次
- アプリケーションの環境構築
- template.yamlを書く
- 実行スクリプトを書く
- デプロイ
- 検証
1. アプリケーションの環境構築
構築は前回の【AWS SAM】Python版入門 を参考にしてください。
(Pipenvを使ったAWS SAMアプリケーションの初期化手順です。)
2. template.yamlを書く
リソース
今回作成するリソースは以下です
- Table(DynamoDB)
- Lambda用Role(IAM)
- Lambda
リソース定義
では、上記リソースを作成するtemplate.yamlを書いていきましょう。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
dynamo-api
Sample SAM Template for dynamo-api
# Lambdaのタイムアウト設定
Globals:
Function:
Timeout: 60
# テーブル名、関数名などの定義
Parameters:
DynamoAPIFunctionName:
Type: String
Default: dynamo-api
DynamoTableName:
Type: String
Default: DynamoApiTable
# リソースの定義
Resources
DynamoTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref DynamoTableName
# HashキーとRangeキーの定義(カラム名と型)
AttributeDefinitions:
- AttributeName: group
AttributeType: S
- AttributeName: name
AttributeType: S
KeySchema:
- AttributeName: group
KeyType: HASH
- AttributeName: name
KeyType: RANGE
# 料金モード指定(PAY_PER_REQUESTは従量課金)
BillingMode: PAY_PER_REQUEST
DynamoAPIFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- 'lambda.amazonaws.com'
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'
# DynamoDBテーブルへの読み取り許可(こうすると、今回作成するテーブルの読み取りのみを許可できます)
Policies:
- PolicyName: 'DynamoApiTablePolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Get*
- dynamodb:Query
Resource: !GetAtt DynamoTable.Arn
DynamoAPIFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Ref DynamoAPIFunctionName
Role: !GetAtt DynamoAPIFunctionRole.Arn
# app.pyがあるディレクトリパス
CodeUri: dynamo-api/
# ハンドラーのパス(むやみに変更するとミスしやすい、、、そっとしておくのが吉)
Handler: app.lambda_handler
Runtime: python3.7
# APIGatewayの設定(SAM独特の記述)
Events:
DynamoApi:
Type: Api
Properties:
Path: /dynamo-api/v1/
Method: get
# Outputsに指定したパラメータは、他のtemplateへデータを渡したりするのが本来の役目ですが
# 今回はデプロイ完了時にAPIのエンドポイントを出力するために使っています。
Outputs:
DynamoApi:
Description: "API Gateway endpoint URL for Prod stage for Dynamo API function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/dynamo-api/v1/"
LambdaやAPIGatewayの設定なんかは、ほとんど雛形と同じなので楽に書けます。
3. 実行スクリプトを書く
ライブラリのインストール
まずは、必要なライブラリをインストールします。
$ pipenv install requests boto3
APIの処理を書く
次に、実際にDynamoテーブルからデータを取ってくる処理を作っていきます。
GETパラメータに指定されたgroup
とname
でDynamoテーブルを検索、その値を返す
という単純な仕組みです。
name
は前方一ができるようにしてみました。
import json
import json
import boto3
from boto3.dynamodb.conditions import Key
from http import HTTPStatus as status
DYNAMODB_TABLE_NAME = 'DynamoApiTable'
def lambda_handler(event, context):
table = boto3.resource('dynamodb').Table(DYNAMODB_TABLE_NAME)
params = event.get('queryStringParameters')
results = dynamo_search(table, params)
if results is None:
return {
"statusCode": status.BAD_REQUEST,
"body": json.dumps({
"error": "Bad Request"
}, ensure_ascii=False),
}
return {
"statusCode": status.OK,
"body": json.dumps({
"results": results
}, ensure_ascii=False),
}
def dynamo_search(table, params):
if params.get('group'):
keyConditionExpression = Key('group').eq(params.get('group'))
if params.get('name'):
keyConditionExpression &= Key('name').begins_with(params.get('name'))
return table.query(
KeyConditionExpression=keyConditionExpression).get('Items', [])
return None
4. デプロイ
(余談) Pipfile to requirements.txt
Pipfile
からrequirements.txt
に変換したい、、、
そんな時はこのコマンドで変換可能です。
(boto3もLambdaに最初から用意されてるし、あまり使い所ないかも。。)
$ pipenv lock -r > requirements.txt
今回は、デフォルトのrequirements.txt
のままで大丈夫です
ビルド
$ sam build
Building resource 'DynamoAPIFunction'
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
デプロイ
~/.aws/credentials
で設定を分けている方は、--profile オプションを付けてください。
--guided
オプションは初回のみでOKです。(samconfig.toml
に設定が保存されるので)
$ sam deploy --guided
Stack dynamo-api-stack outputs:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OutputKey-Description OutputValue
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DynamoApi - API Gateway endpoint URL for Prod stage for Dynamo API function https://hogehoge/Prod/dynamo-api/v1/
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
エンドポイントもちゃんと出力されてますね〜
5.検証
Dynamoにデータを登録
今回は(も)、推しのグループ名と名前を登録しました。
group(Hash) | name(Range) |
---|---|
ハニーストラップ | 周防パトラ |
ハニーストラップ | 堰代ミコ |
ホロライブ | 白上フブキ |
... | ... |
APIを叩いてみる
出力されたエンドポイントにリクエストを送ってみます。見にくいのでjq
で整形しましょう。
(curl
面倒だよって人はブラウザで)
まずはgroup
(Hashキー)指定です。
$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=ハニーストラップ' | jq
{
"results": [
{
"name": "周防パトラ",
"group": "ハニーストラップ"
},
{
"name": "堰代ミコ",
"group": "ハニーストラップ"
},
{
"name": "島村シャルロット",
"group": "ハニーストラップ"
},
{
"name": "西園寺メアリ",
"group": "ハニーストラップ"
}
]
}
取れました
もちろん、前方一致でname
(Rangeキー)も検索できます。
$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=ハニーストラップ' \
--data-urlencode 'name=周防' | jq
{
"results": [
{
"name": "周防パトラ",
"group": "ハニーストラップ"
}
]
}
取れました!完璧です!
終わりに
今回は、単純なAPIを作ってみました。次回はpytest
でテストをしてみようと思います。
最後まで読んでいただきありがとうございました!