11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

内容

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

目次

  1. アプリケーションの環境構築
  2. template.yamlを書く
  3. 実行スクリプトを書く
  4. デプロイ
  5. 検証

1. アプリケーションの環境構築

構築は前回の【AWS SAM】Python版入門 を参考にしてください。
(Pipenvを使ったAWS SAMアプリケーションの初期化手順です。)

2. template.yamlを書く

リソース

今回作成するリソースは以下です

  • Table(DynamoDB)
  • Lambda用Role(IAM)
  • Lambda

リソース定義

では、上記リソースを作成するtemplate.yamlを書いていきましょう。

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パラメータに指定されたgroupnameでDynamoテーブルを検索、その値を返す
という単純な仕組みです。
nameは前方一ができるようにしてみました。

app.py
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に最初から用意されてるし、あまり使い所ないかも。。:thinking:

$ pipenv lock -r > requirements.txt

今回は、デフォルトのrequirements.txtのままで大丈夫です:sweat_smile:

ビルド

$ 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面倒だよって人はブラウザで:information_desk_person:

まずはgroup(Hashキー)指定です。

$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=ハニーストラップ' | jq

{
  "results": [
    {
      "name": "周防パトラ",
      "group": "ハニーストラップ"
    },
    {
      "name": "堰代ミコ",
      "group": "ハニーストラップ"
    },
    {
      "name": "島村シャルロット",
      "group": "ハニーストラップ"
    },
    {
      "name": "西園寺メアリ",
      "group": "ハニーストラップ"
    }
  ]
}

取れました:clap:

もちろん、前方一致でname(Rangeキー)も検索できます。

$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=ハニーストラップ' \
--data-urlencode 'name=周防' | jq
{
  "results": [
    {
      "name": "周防パトラ",
      "group": "ハニーストラップ"
    }
  ]
}

取れました!完璧です!

終わりに

今回は、単純なAPIを作ってみました。次回はpytestでテストをしてみようと思います。
最後まで読んでいただきありがとうございました!

前回

【AWS SAM】Python版入門

11
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?