0
2

Python Lambda DynamoDB ServerlessFrameworkでAPI作ってみた(ローカル開発)

Last updated at Posted at 2023-10-05

はじめに

Python / Lambda / DynamoDB / ServerlessFramework でのAPI開発手順を記しておきます。

AWSの方から「Lambda × Pythonは起動が速く(コールドスタートが起きない)相性がいい」と伺っていたので、デプロイして計測するのが楽しみです!

環境

  • macOS Ventura
  • Python 3.11.4
  • ServerlessFramework 3.34.0
  • npm 8.19.2
  • node v18.18.0
バージョン確認コマンド
$ python3 --version
$ python3 -m pip -V

Lambda作成

1. 事前準備

$ mkdir project-v1 #プロジェクト用ルートディレクトリ作成
$ npm install -g serverless #必要なプラグインのインストール
$ npm install --save serverless-iam-roles-per-function serverless-offline serverless-prune-plugin
$ sls plugin install -n serverless-python-requirements
$ sls plugin install -n serverless-dynamodb
$ sls dynamodb install

$ sls create --template aws-python3 #Serverlessプロジェクトを作成

去年使用していた serverless-dynamodb-local はもう使えないようです(npm参照)。

2. Serverless定義

可読性・メンテナンス性を考慮し、定義元ファイルを個別に分けています。

serverless.yml
service: project-v1
frameworkVersion: '3'

provider:
  name: aws
  runtime: python3.11
  region: ap-northeast-1
  stage: stg

plugins:
  - serverless-dynamodb
  - serverless-offline # dynamodb より offline を後ろに記載
  - serverless-iam-roles-per-function
  - serverless-prune-plugin
  - serverless-python-requirements

custom:
  stage: ${opt:stage, self:provider.stage}
  dynamodb: ${file(./config/setting.yml):dynamodb}
  seed: ${file(./config/setting.yml):dynamodb.seed}
  resources: ${file(./config/resources.yml)}
  serverless-offline:
    stage: "local"
  prune:
    automatic: true
    number: 5

resources:
  - ${file(./config/dynamodb.yml)}

functions:
  Hello:
    handler: main.handlers.handler.lambda_handler
    name: Hello
    description: Pythonテスト
    events:
      - http:
          path: hello
          method: GET
          cors: true
    iamRoleStatements:
      - ${file(./config/iam.yml):getItemTables}

以下、serverless.yml内で読み込む複数ファイルを定義

config/setting.yml
dynamodb:
  stages:
    - local
  port: 8000
  docker: false
  start:
    inMemory: true
    migrate: true
    seed: true
  seed:
    test:
      sources:
        - table: ${self:custom.resources.HogesTable.name}
          sources: [./main/resources/migrations/Hoges.json]
        - table: ${self:custom.resources.FugasTable.name}
          sources: [./main/resources/migrations/Fugas.json]

config/dynamodb.yml
Resources:
  HogesTable:
    Type: AWS::DynamoDB::Table
    ////
  FugasTable:
    Type: AWS::DynamoDB::Table
    ////
config/resources.yml
HogesTable:
  name: Hoges
  arn: "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/Hoges*"

FugasTable:
  name: Fugas
  arn: "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/Fugas*"
config/iam.yml
getItemTables:
  Effect: Allow
  Action:
    - dynamodb:getItem
  Resource:
    - ${self:custom.resources.HogesTable.arn}

3. boto3 (AWS SDK for Python) インストール

$ pip3 install boto3

##Successfully installed boto3-1.28.59 botocore-1.31.59 jmespath-1.0.1 python-dateutil-2.8.2 s3transfer-0.7.0 six-1.16.0 urllib3-1.26.17

4. Handler作成

main.handlers.handler.py
import json
from main.models.aws_dynamodb import AwsDynamoDb


def lambda_handler(event, context):
    dynamoDb = AwsDynamoDb()
    hoges = dynamoDb.get_item("table_name", "sk_name", "sk_value")

    print(f"Hoges: {hoges}")

    return {"statusCode": 200, "body": json.dumps(hoges)}

5. AwsDynamoDbクラス作成

main.models.aws_dynamodb.py
import boto3


class AwsDynamoDb:
    def __init__(self):
        self.dynamodb = boto3.resource(
            "dynamodb",
            region_name="ap-northeast-1",
            endpoint_url="http://localhost:8000",
        )

    # 値を一つ取得
    def get_item(self, table_name, key, value):
        try:
            table = self.dynamodb.Table(table_name)
            item = table.get_item(Key={key: value})
            return item.get("Item")
        except Exception as e:
            print(f"Error getting item from DynamoDB: {e}")
            return "エラー"

挙動確認

1. ローカル起動

$ sls offline start --stage local
$ sls dynamodb start --stage local

2. 疎通確認

$ curl http://localhost:3000/local/hello

参考

さいごに

サクッとAPI開発するのなら、とりあえず上記で試すのはありかなと思っています。
slsnpm でのインストールは、インストール時期によって使えない場合もあるので、公式ドキュメントをご確認ください。

Java歴約2年で、Pythonを使うのは初めてでした。
本記事には数行しかPython記述はないですが、これから外部連携したり、大きくしたりで開発続けます。

ユニットテスト・規約チェック・ログ関連・バージョン管理ツールは入れる予定です。
ディレクトリ構造の最適解はまだ持ち合わせていません。

ここ間違ってるよ!もっとこうしたらどう?といったご指摘やアドバイスはいつでも歓迎です!


(感想)2年半前のRuby以来の「動的型付け言語」でした。

🙆‍♀️
記述量少なくて済むの感激!書きやすい読みやすい!
(データ型の宣言ないことは少し違和感、、完全にJavaに慣れてしまいました)

🙅‍♀️
エラーやバグが、コンパイルではなくプログラム実行時にしか分からない。
コンパイルに時間を要すのは退屈だったけど、そこでエラー教えてくれるのありがたかった。

結果、どちらも一長一短で楽しいです。もう少しPython続けてみます。

0
2
0

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
0
2