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"
}
]
}
For construction, refer to the previous [AWS SAM] Introduction to Python version. (This is the procedure for initializing an AWS SAM application using Pipenv.)
The resources to be created this time are as follows
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.
First, install the required libraries.
$ pipenv install requests boto3
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
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:
$ 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
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 ~
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 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!
This time, I made a simple API. Next time, I will test with pytest
.
Thank you for reading until the end!
[AWS SAM] Introduction to Python
Recommended Posts