[PYTHON] [AWS SAM] Create API with DynamoDB + Lambda + API Gateway

Contents

Let's build an API that fetches DynamoDB data with AWS SAM

In short, from a table like this ...

group(Hash) name(Range)
group1 name1
group1 name2
group2 name3

I want to take it like this.

curl https://hogehoge/Prod/dynamo-api/v1/?group=group1&name=name1

{
    "result": [
        {
            "name": "group1",
            "group": "name1"
        }
    ]
}

environment

table of contents

  1. Application environment construction
  2. Write template.yaml
  3. Write an execution script
  4. Deploy
  5. Verification

1. Application environment construction

For construction, refer to the previous [AWS SAM] Introduction to Python version. (This is the procedure for initializing an AWS SAM application using Pipenv.)

2. Write template.yaml

resource

The resources to be created this time are as follows

Resource definition

Now, let's write template.yaml to create the above resource.

template.yaml


AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  dynamo-api

  Sample SAM Template for dynamo-api

#Lambda timeout settings
Globals:
  Function:
    Timeout: 60

#Definition of table name, function name, etc.
Parameters:
  DynamoAPIFunctionName:
    Type: String
    Default: dynamo-api
  DynamoTableName:
    Type: String
    Default: DynamoApiTable

#Resource definition
Resources
  DynamoTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Ref DynamoTableName
      #Hash key and Range key definitions (column name and type)
      AttributeDefinitions:
        - AttributeName: group
          AttributeType: S
        - AttributeName: name
          AttributeType: S
      KeySchema:
        - AttributeName: group
          KeyType: HASH
        - AttributeName: name
          KeyType: RANGE
      #Charge mode designation (PAY_PER_REQUEST is pay-as-you-go)
      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'
      #Permission to read DynamoDB table (this allows you to only read the table you are creating this time)
      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.Directory path where py is
      CodeUri: dynamo-api/
      #Handler path (It's easy to make a mistake if you change it carelessly ... It's good to leave it alone)
      Handler: app.lambda_handler
      Runtime: python3.7
      #API Gateway settings (SAM-specific description)
      Events:
        DynamoApi:
          Type: Api
          Properties:
            Path: /dynamo-api/v1/
            Method: get

#The original role of the parameters specified in Outputs is to pass data to other templates.
#This time, it is used to output the API endpoint when the deployment is completed.
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/"

The settings of Lambda and API Gateway are almost the same as the template, so you can write it easily.

3. Write an execution script

Library installation

First, install the required libraries.

$ pipenv install requests boto3

Write API processing

Next, we will create a process that actually fetches data from the Dynamo table. Search the Dynamo table with the group and name specified in the GET parameter and return its value It is a simple mechanism. I tried to make name the front one.

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. Deploy

(Digression) Pipfile to requirements.txt

I want to convert from Pipfile to requirements.txt ... In such a case, you can convert with this command. (Boto3 is also prepared in Lambda from the beginning, so it may not be used much ..: thinking :)

$ pipenv lock -r > requirements.txt

This time, you can keep the default requirements.txt: sweat_smile:

Build

$ 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

Deploy

If you have separate settings for ~ / .aws / credentials, add the --profile option. The --guided option is OK only for the first time. (Because the settings are saved in 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/                     
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

The endpoint is also output properly ~

5. Verification

Register data in Dynamo

This time (also), I registered the recommended group name and name.

group(Hash) name(Range)
Honey strap Suou Patra
Honey strap Mico Sekishiro
Hololive Shirakami Fubuki
... ...

Try hitting the API

Try sending a request to the output endpoint. It's hard to see, so let's format it with jq. (People in the browser because curl is troublesome: information_desk_person :)

First, specify group (Hash key).

$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=Honey strap' | jq

{
  "results": [
    {
      "name": "Suou Patra",
      "group": "Honey strap"
    },
    {
      "name": "Mico Sekishiro",
      "group": "Honey strap"
    },
    {
      "name": "Shimamura Charlotte",
      "group": "Honey strap"
    },
    {
      "name": "Mary Saionji",
      "group": "Honey strap"
    }
  ]
}

I got it: clap:

Of course, you can also search for name (Range key) with a prefix match.

$ curl https://hogehoge/Prod/dynamo-api/v1/ \
--get \
--data-urlencode 'group=Honey strap' \
--data-urlencode 'name=Suo' | jq
{
  "results": [
    {
      "name": "Suou Patra",
      "group": "Honey strap"
    }
  ]
}

I got it! It's perfect!

At the end

This time, I made a simple API. Next time, I will test with pytest. Thank you for reading until the end!

Last time

[AWS SAM] Introduction to Python

Recommended Posts

[AWS SAM] Create API with DynamoDB + Lambda + API Gateway
[AWS] Create API with API Gateway + Lambda
Create API with Python, lambda, API Gateway quickly using AWS SAM
Easy REST API with API Gateway / Lambda / DynamoDB
LINE BOT with Python + AWS Lambda + API Gateway
[AWS] Try tracing API Gateway + Lambda with X-Ray
Send images taken with ESP32-WROOM-32 to AWS (API Gateway → Lambda → S3)
How to create a serverless machine learning API with AWS Lambda
View images on S3 with API Gateway + Lambda
Serverless application with AWS SAM! (APIGATEWAY + Lambda (Python))
Amazon API Gateway and AWS Lambda Python version
Try implementing a Cisco Spark bot with AWS Lambda + Amazon API Gateway (Python)
[AWS] Play with Step Functions (SAM + Lambda) Part.3 (Branch)
Create a Layer for AWS Lambda Python with Docker
[AWS] Play with Step Functions (SAM + Lambda) Part.1 (Basic)
[AWS] Play with Step Functions (SAM + Lambda) Part.2 (Parameter)
Create an API with Django
AWS CDK-Lambda + API Gateway (Python)
AWS Lambda with PyTorch [Lambda import]
Quickly take a query string with API Gateway-> Lambda (Python)
AWS Amplify + API Gateway + Lambda + Python returns a normal response
[AWS] Try adding Python library to Layer with SAM + Lambda (Python)
Create Awaitable with Python / C API
Using Lambda with AWS Amplify with Go
Create API using hug with mod_wsgi
Notify HipChat with AWS Lambda (Python)
Create an alias for Route53 to CloudFront with the AWS API
I tried ChatOps with Slack x API Gateway x Lambda (Python) x RDS
[Python] I wrote a REST API using AWS API Gateway and Lambda.
Create a REST API to operate dynamodb with the Django REST Framework
LINE BOT (Messaging API) development with API Gateway and Lambda (Python) [Part 2]
Create Cognito user list in S3 with SQS Deploy queue function and API to Lambda with SAM
I tried to delete bad tweets regularly with AWS Lambda + Twitter API
Automatically create Python API documentation with Sphinx
[AWS] Create a Python Lambda environment with CodeStar and do Hello World
Let's create a chat function with Vue.js + AWS Lambda + dynamo DB [AWS settings]
Manipulate DynamoDB data with Lambda (Node & Python)
I tried to make "Sakurai-san" a LINE BOT with API Gateway + Lambda
Understanding from the mechanism Twilio # 3-1 --AWS API Gateway + Lambda implementation Walkthrough (Part 1)
[AWS] Link Lambda and S3 with boto3
[Python] Quickly create an API with Flask
Connect to s3 with AWS Lambda Python
Create Page / Todo Block with Notion API
Create a private repository with AWS CodeArtifact
[AWS] Do SSI-like things with S3 / Lambda
Python + Selenium + Headless Chromium with aws lambda
I just did FizzBuzz with AWS Lambda
Create a bot with AWS Lambda that automatically starts / stops Instances with specific tags
Regular serverless scraping with AWS lambda + scrapy Part 1.8
Serverless scraping using selenium with [AWS Lambda] -Part 1-
I tried connecting AWS Lambda with other services
Infrastructure construction automation with CloudFromation + troposphere + AWS Lambda
Pass Cognito Id to Lambda via API Gateway
Create an API server quickly with Python + Falcon
A note that connects to Lambda via AWS API Gateway (HTTP API) to process POST data
Dynamic HTML pages made with AWS Lambda and Python
Create Amazon Linux with AWS EC2 and log in
Create Python version Lambda function (+ Lambda Layer) with Serverless Framework
How to set layer on Lambda using AWS SAM
Deploy Python3 function with Serverless Framework on AWS Lambda
Write multiple records to DynamoDB with Lambda (Python, JavaScript)