見出し画像

AWS LambdaからDynamoDBをさわってみる

こんばんは。
本日はLambdaからDynamoDBを操作してみたいと思います。

DynamoDBはスキーマレスのNoSQLデータベースです。
通常はAppSyncを介してGraphQLを動かすためのデータベースとして使用されますが、DynamoDB自体は一般的なNoSQLデータベースです。

LambdaからDynamoDBへのアクセスは非常に簡単です。
処理の流れとしては、Lambda → AppSync → DynamoDBという順序でデータを投入することができます。

今回はDynamoDBの作成とAppSyncの設定を行い、Lambdaで処理を書いてDynamoDBにデータを投入してみたいと思います。

1.DynamoDBの設定

まずはいつものように、Serverless Frameworkを使用してDynamoDBのテーブルを作成します。
DynamoDBの設定は別途ymlファイルに記述し、resourcesセクションで読み込む形式となります。

# serverless.yml

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

Resources:
 sampleTable:
   Type: AWS::DynamoDB::Table
   Properties:
     TableName: sampleTable
     AttributeDefinitions:
       - AttributeName: id
         AttributeType: S
     KeySchema:
       - AttributeName: id
         KeyType: HASH
     ProvisionedThroughput:
       ReadCapacityUnits: 1
       WriteCapacityUnits: 1

同時にLambdaからDynamoDBにアクセスするためには、LambdaにDynamoDBへのアクセス権限を与える必要があります。
そのためには、ロールに適切な許可を追加します。

2.AppSyncの設定

AppSyncの設定をServerless Frameworkのymlファイルに書くためには、AppSyncのプラグインをインストールし、pluginsに追記する必要があります。

$ yarn add -D serverless-appsync-plugin

plugins:
 - serverless-bundle
 - serverless-offline
 - serverless-dotenv-plugin
 - serverless-layers
 - serverless-step-functions
 - serverless-appsync-plugin // 追記
 

custom:
 // ここから追記
 appSync:
   name: ${self:service}-${self:provider.stage}
   region: ap-northeast-1
   authenticationType: API_KEY // API_KEYにする
   mappingTemplatesLocation: resolvers // resolver処理の入ったディレクトリを指定
   mappingTemplates: // resolver処理の中無を指定
     -
       type: Mutation
       field: createStatus
       dataSource: transactionsTable

   schema: schema.graphql

   dataSources: // AppSyncから触るデータソースを指定
     - type: AMAZON_DYNAMODB
       name: transactionsTable
       config:
         tableName: transactionsTable
         iamRoleStatements:
           - Effect: Allow
             Action:
               - dynamodb:*
             Resource:
               - arn:aws:dynamodb:${self:provider.region}:*:table/transactionsTable
               - arn:aws:dynamodb:${self:provider.region}:*:table/transactionsTable/*
         region: ap-northeast-1

3.AppSyncのresolverを書く

そもそも"resolver"とは、以下のようなものです。

AppSyncのリゾルバーは、GraphQLのスキーマで受け付けたリクエストに対し実際にデータ操作を行う部分で、Apache Velocity Template Language(VTL)というプログラミング言語で記述することができます。

https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/configuring-resolvers.html

リクエストを受けた際に、受けたデータの操作方法を決定することができるのが"resolver"です。
しかし、VTLという微妙なシンタックスの言語で書かなければならないことや、resolver自体が不要であるほうが良いという要望もあるかもしれません。それでも、我慢してresolverを書いていきましょう。

今回のケースでは、単純にPOSTするだけなので、graphqlのスキーマとMutationの定義だけを作成します。
ただし、このresolverはリクエストとレスポンスの両方が必要です。

// shcema.graphql

type Status {
 id: ID!
 status: String
}

input CreateStatusInput {
 id: ID!
 status: String
}

type Mutation {
 createStatus(input: CreateStatusInput!): Status!
}
// Mutation.createStatus.request.vtl - リクエスト
{
 "version": "2017-02-28",
 "operation": "PutItem",
 "key": {
   "id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
 },
 "attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
 "condition": {
   "expression": "attribute_not_exists(#id)",
   "expressionNames": {
     "#id": "id"
   }
 }
}

// Mutation.createStatus.response - レスポンス
$util.toJson($context.result)

これでDynamoDBの設定とAppSyncの設定が完了しました。
一旦deployコマンドを実行してDynamoDBのテーブルを作成しましょう。
また、AppSyncのエンドポイントとAPI-KEYも生成しておきます。

4.Lambdaに処理を書く

Lambdaのコードを書いていきましょう。
DynamoDBを操作する際には、以下のようなの2つの方法があります。

● AWS.DynamoDB.DocumentClientを使う方法
● 普通にエンドポイントにPOSTする方法

AWS.DynamoDB.DocumentClientは非常に便利で、JavaScriptのデータ型を自動的にDynamoDB上のデータ型に変換してくれます。
しかし、なぜかLambdaからAppSyncを叩くことがうまくいきませんでした。
そこで、今回はfetchライブラリを使用して直接エンドポイントにPOSTする方法を試してみたいと思います。
まずはnode-fetchをインストールしましょう。

$ yarn add -D node-fetch

では、次にDynamoDBへのPUT処理を実装していきましょう。

# index.ts

import { scanItems, getItem, putItem, deleteItem } from './services.ts';
import fetch from 'node-fetch';

//-----------------------------------
// テーブルにデータを保存する
//-----------------------------------
export const create = async (params: any) => {

  const body = JSON.parse(event.body);

  // PUTする内容
  const variables = {
    input: {
      id: `evt_${body.id}`,
      status: 'Do Mutation.'
    }
  }
  
  // Mutationイベント
  const query = `
    mutation CreateStatus($input: CreateStatusInput!) {
      createStatus(input: $input) {
        id
        status
      }
    }
  `;

  // PUTする処理
  const response = await fetch('appsyncのコンソールから確認してgraphqlエンドポイントを入れる', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'X-Api-Key': 'appsyncのコンソールから確認してAPI-KEYを入れる'
    },
    body: JSON.stringify({
      query,
      variables
    })
  });
  
  // レスポンス
  const res = await response.json();
  console.log(res);
  
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Mutation Done."
    }),
  };
}

それでは、書いたコードをデプロイしてみましょう。
Lambda関数はAPI Gatewayをトリガーとして実行されます。
クライアント側からAPIを呼び出す際には、IDをリクエストのボディに含めることで、Lambda関数の引数であるeventにJSON形式で渡されます。
渡されたevent.bodyをパースしてIDを取り出しましょう。

取得したIDを利用して、自由にDynamoDBにPUT操作を行うことができます。
これにより、クライアント側からIDを渡すことで、DynamoDBへの自由なPUT操作が可能になりました。

このコードをデプロイすることで、API Gatewayのエンドポイントが生成されます。
クライアントからエンドポイントに対してリクエストを送信し、IDをボディに含めることで、DynamoDBへのPUT操作を実行できます。

以上で、クライアント側からIDを渡してDynamoDBへの自由なPUT操作が可能となりました。

それでは。

いいなと思ったら応援しよう!