13
5

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 1 year has passed since last update.

【AWS CDK】API GatewayからLambdaを通さずに直接DynamoDBにアクセスしGetItemするAPIを作ってみた

Last updated at Posted at 2022-05-31

DynamoDBから値を取ってきて返すAPIを作成するにあたり、Lambdaを通さずに直接DynamoDBから値を取れることを知ったので試してみました。

作るもの

API GatewayからLambdaを通さずにDynamoDBに直接アクセスし、API Gateway + DynamoDBでgetItemするAPIを作成します。
今回は、GET books/{title}を叩くと以下のような形でDynamoDBに保存されている値を整形して返却してくれるようなAPIを作成していきます。

{
  "title": "how_to_book",
  "price": 1000
}

構成

シンプルな構成です。
architecture.png

環境

cdk --version
2.10.0 (build e5b301f)

tsc --v
Version 3.9.10

完成系

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as apigateway from "aws-cdk-lib/aws-apigateway";
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as iam from 'aws-cdk-lib/aws-iam';

export class TestBookStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    // DynamoDB
    const table = new dynamodb.Table(this, "dynamoDB", {
      tableName: "Book",
      partitionKey: { name: 'title', type: dynamodb.AttributeType.STRING }
    });

    // API Gateway
    const api = new apigateway.RestApi(this, "bookApi", {
      deployOptions: {
        stageName: "dev",
        metricsEnabled: true,
        dataTraceEnabled: true,
      }
    });

    // api -> DynamoDBのIAMポリシー
    const credentialRole = new iam.Role(this, 'role', {
      assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
    });
    credentialRole.attachInlinePolicy(
      new iam.Policy(this, 'getItemPolicy', {
        statements: [
          new iam.PolicyStatement({
            actions: [
              "dynamodb:GetItem"
          ],
            effect: iam.Effect.ALLOW,
            resources: [table.tableArn]
          })
        ]
      })
    );

    // apiのリソースの用意
    const allResources = api.root.addResource('books');
    const bookResource = allResources.addResource('{title}');

    // titleを指定してitemを1件取得するapi
    bookResource.addMethod('GET', new apigateway.AwsIntegration({
      service: 'dynamodb',
      action: 'GetItem',
      options: {
        credentialsRole: credentialRole,
        requestTemplates: {
          'application/json': `{
              "Key": {
                "title": {
                  "S": "$input.params('title')"
                }
              },
              "TableName": "Book"
            }`
        },
        integrationResponses: [
          {
            statusCode: '200',
            responseTemplates: {
              'application/json': `{
                "title": "$input.path('$').Item.title.S",
                "price": $input.path('$').Item.price.N,
              }`
            }
          }
        ]
      }
    }),
    {
      methodResponses: [
        {
          statusCode: '200',
        }
      ]
    });
  }
}

DynamoDB

まずはDynamoDBを作成します。
Bookという名前のテーブルを作成し、titleという名前のパーティションキーを用意します。

const table = new dynamodb.Table(this, "dynamoDB", {
  tableName: "Book",
  partitionKey: { name: 'title', type: dynamodb.AttributeType.STRING },
});

API Gateway

次にAPI Gatewayを作成します。
少し長くなるので、小分けに説明を書いていきます。

RestApi

REST APIで実装をしていくので、まずはそれを記述します。

const api = new apigateway.RestApi(this, "bookApi", {
  deployOptions: {
    stageName: "dev",
    metricsEnabled: true,
    dataTraceEnabled: true
  }
});

addResource

今回はGET books/{title}といった形のAPIを作成したいので、それを記述します。

const allResources = api.root.addResource('books');
const bookResource = allResources.addResource('{title}');

このように書くと、以下のようなかたちでリソースが用意されます。

resource.png

addMethod

DynamoDBにGetItemをしに行くAPIなので、それを記述します。
統合タイプにAWSサービス、サービスにdynamodbを指定することで、API Gatewayから直接DynamoDBへアクセスすることができます。

bookResource.addMethod('GET', new apigateway.AwsIntegration({
  service: 'dynamodb',
  action: 'GetItem'
}));

ちなみに、全件取得したい場合には以下のような書き方になります。

allResources.addMethod('GET', new apigateway.AwsIntegration({
  service: 'dynamodb',
  action: 'Scan'
}));

統合リクエスト

統合リクエストを利用し、バックエンド(DynamoDB)に情報を渡す際にどのような変換を行うのか定義していきます。
今回は、クライアントからのリクエストをDynamoDBのGetItem APIリクエストに変換します。

$input.params('title')と書くことで、GET books/{title}{title}の部分を取得することができます。
これを利用し、DynamoDBのBookというテーブルから、パーティションキーであるtitleの値を指定してGetItemします。

requestTemplates: {
  'application/json': `{
    "Key": {
      "title": {
        "S": "$input.params('title')"
      }
    },
    "TableName": "Book"
  }`
}

全件取得ではこのように書くことができます。

 requestTemplates: {
  'application/json': `{
    "TableName": "Book"
  }`
}

統合レスポンス

今回は、DynamoDB からのレスポンスをそのままクライアントに返すのではなく、整形してから返していきます。
そのために統合レスポンスを利用し、DynamoDBから返ってきた情報の変換をしていきます。

$input.path('$')にDynamoDBから取ってきた値が入ってくるため、それを利用して整形を行います。

integrationResponses: [
  {
    statusCode: '200',
    responseTemplates: {
      'application/json': `{
         "title": "$input.path('$').Item.title.S",
         "price": $input.path('$').Item.price.N,
      }`
    }
  }
]

メソッドレスポンス

今回は正常終了パターンのみ設定しておきます。

methodResponses: [
  {
    statusCode: '200',
  }
]

IAMポリシー

このままではAPI GatewayにDynamoDBを触る権限がないため、ロールを作成しアタッチします。

DynamoDBにGetItemする許可をするIAMポリシーを作成し、ロールに紐付けます。
全件取得したい場合には、actions"dynamodb:Scan"を設定します。

const credentialRole = new iam.Role(this, 'role', {
  assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
});
credentialRole.attachInlinePolicy(
  new iam.Policy(this, 'getItemPolicy', {
    statements: [
      new iam.PolicyStatement({
        actions: [
          "dynamodb:GetItem"
        ],
        effect: iam.Effect.ALLOW,
        resources: [table.tableArn]
      })
    ]
  })
);

上記で用意したロールを以下のようにしてアタッチします。

bookResource.addMethod('GET', new apigateway.AwsIntegration({
  service: 'dynamodb',
  action: 'GetItem',
  options: {
      credentialsRole: credentialRole,
  }
}));

動作確認

まずは下準備として、スタックのデプロイを行い、作成したDynamoDBにデータを入れておきます。
dynamoDB.png

データの準備ができたら、実際にAPIを叩いてみたいと思います。
今回はコンソール上から実施していきます。
API Gatewayのテスト呼び出しの画面を表示し、先程用意したデータに存在するtitleを{title}の部分に入力してテストボタンを押下します。
API_Gateway_test.png

期待した結果が返ってきました!
result.png

まとめ

API GatewayからLambdaを通さずにDynamoDBに直接アクセスし、API Gateway + DynamoDBでgetItemするAPIを作成しました。
複雑な変換などを行いたい場合はLambdaを利用した方が有効な場合もありますが、今回のような単純なものであればAPI Gateway + DynamoDBの構成で充分だと感じました。

参考資料

https://qiita.com/ma2shita/items/351746f2b6c414f9d5ab
https://dev.to/elthrasher/aws-cdk-api-gateway-service-integration-with-dynamodb-2ek0

13
5
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
13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?