APIGatewayから直接DynamoDBにCRUD操作を行う方法

APIGatewayから直接DynamoDBにCRUD操作を行う方法

Clock Icon2025.04.18

概要

こんにちは、クラスメソッド製造ビジネステクノロジー部の田中聖也です
意外と知られていないですが、AWS統合を使用することでAPIGatewayからDynamoDBやS3を直接操作出来ることはご存知でしょうか?
今回はAPIGatewayからLmabdaを経由せず、直接DynamoDBに対してCRUD操作を実施したいと思います
アーキテクチャ

やってみた

CDK

本来は統合レスポンスを定義する必要がありますが、今回ここは省略します
とりあえず、APIGatewayからDynamoDBが操作出来るのを目標とします

stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs'
import { 
  aws_apigateway,
  aws_dynamodb,
  aws_iam,
  RemovalPolicy
 } from 'aws-cdk-lib';

const PREFIX = 'Attempt';

export class AttemptStackStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const userTable = new aws_dynamodb.Table(this, `${PREFIX}-UserTable`,{
      tableName: `${PREFIX}-UserTable`,
      partitionKey: {name: 'userId', type: aws_dynamodb.AttributeType.STRING},
      billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY
    });

    const apiGatewayExecRole = new aws_iam.Role(this, `${PREFIX}-ApiGateway-Role`, {
      roleName: `${PREFIX}-ApiGateway-Role`,
      assumedBy: new aws_iam.ServicePrincipal("apigateway.amazonaws.com"),
    });
    const apiGatewayPolicy = new aws_iam.Policy(this, `${PREFIX}-ApiGateway-Policy`, {
      statements: [
        new aws_iam.PolicyStatement({
          actions: ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem"],
          effect: aws_iam.Effect.ALLOW,
          resources: [userTable.tableArn]
        })
      ]
    });
    apiGatewayExecRole.attachInlinePolicy(apiGatewayPolicy);

    const api = new aws_apigateway.RestApi(this, `${PREFIX}-Api`, {
      restApiName: `${PREFIX}-Rest-Api`,
      description: 'Attempt API'
    });

    const usersResource = api.root.addResource('users');

    const createUserModel = api.addModel('CreateUserModel', {
      schema: {
        type: aws_apigateway.JsonSchemaType.OBJECT,
        properties: {
          userId: {type: aws_apigateway.JsonSchemaType.STRING},
          name: {type: aws_apigateway.JsonSchemaType.STRING},
          age: {type: aws_apigateway.JsonSchemaType.NUMBER}
        },
        required: ['userId', 'name', 'age']
      }
    });

    const updateUserModel = api.addModel('UpdateUserModel', {
      schema: {
        type: aws_apigateway.JsonSchemaType.OBJECT,
        properties: {
          name: {type: aws_apigateway.JsonSchemaType.STRING},
          age: {type: aws_apigateway.JsonSchemaType.NUMBER}
        },
        required: ['name', 'age']
      }
    });

    // Note: ユーザー作成
    const postUserIntegration = new aws_apigateway.AwsIntegration({
      region: this.region,
      service: 'dynamodb',
      action: 'PutItem',
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: apiGatewayExecRole,
        requestTemplates: {
          'application/json': `
          #set($inputRoot = $input.path('$'))
          {
            "TableName": "${userTable.tableName}",
            "Item": {
              "userId": {
                "S": "$inputRoot.userId"
              },
              "name": {
                "S": "$inputRoot.name"
              },
              "age": {
                "N": "$inputRoot.age"
              }
            }
          }`
        },
      }
    });

    usersResource.addMethod('POST', postUserIntegration,
      {
        requestModels: {
          'application/json': createUserModel
        },
        methodResponses: [
          {statusCode: '201'}
        ]
      }
    );

    // Note: ユーザー情報取得
    const getUserIntegration = new aws_apigateway.AwsIntegration({
      region: this.region,
      service: 'dynamodb',
      action: 'GetItem',
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: apiGatewayExecRole,
        requestTemplates: {
          'application/json': `{
            "TableName": "${userTable.tableName}",
            "Key": {
              "userId": {
                "S": "$input.params('userId')"
              }
            }
          }`
        },
        integrationResponses: [
          {
            statusCode: '200',
            responseTemplates: {
              'application/json': `{
                "name": "$input.path('$').Item.name.S",
                "age": "$input.path('$').Item.age.N"
              }`
            }
          }
        ]
      }
    });

    // Note: ユーザー削除
    const deleteUserIntegration = new aws_apigateway.AwsIntegration({
      region: this.region,
      service: 'dynamodb',
      action: 'DeleteItem',
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: apiGatewayExecRole,
        requestTemplates: {
          'application/json': `{
            "TableName": "${userTable.tableName}",
            "Key": {
              "userId": {
                "S": "$input.params('userId')"
              }
            }
          }`
        }
      }
    });

    // Note: ユーザー更新
    const putUserIntegration = new aws_apigateway.AwsIntegration({
        region: this.region,
        service: 'dynamodb',
        action: 'UpdateItem',
        integrationHttpMethod: 'POST',
        options: {
          credentialsRole: apiGatewayExecRole,
          requestTemplates: {
            'application/json': `
            #set($inputRoot = $input.path('$'))
            {
              "TableName": "${userTable.tableName}",
              "Key": {
                "userId": {
                  "S": "$input.params('userId')"
                }
              },
              "ExpressionAttributeNames": {
                "#name": "name",
                "#age": "age"
              },
              "ExpressionAttributeValues": {
                ":name": {"S": "$inputRoot.name"},
                ":age": {"N": "$inputRoot.age"}
              },
              "UpdateExpression": "SET #name = :name, #age = :age"
            }`
          }
        }
    });

    const userIdResource = usersResource.addResource('{userId}');
    userIdResource.addMethod('GET', getUserIntegration, 
      {
        methodResponses: [
          {statusCode: '200'}
        ]
      }
    );
    userIdResource.addMethod('PUT', putUserIntegration,
      {
        requestModels: {
          'application/json': updateUserModel
        },
        methodResponses: [
          {statusCode: '200'}
        ]
      }
    );
    userIdResource.addMethod('DELETE', deleteUserIntegration,
      {
        methodResponses: [
          {statusCode: '204'}
        ]
      }
    );
  }
}

ユーザー作成

作成されたAPIGatewayからユーザーを作成してみます
Post_request

なんか、リクエストが通っていそうですね
Post_response

実際にDynamoDBを確認すると指定したパラメーターでユーザーが作成されているのが分かります
Post_result

ユーザー取得

Get_response

ユーザー更新

Put_request

ユーザー削除

Delete_response

まとめ

APIGatewayから直接DynamoDBに対してCRUD操作が出来ることを確認しました
単体操作だけでなくTransactWriteItemsなども利用出来るので、色んな場面で使用できそうですね

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.