LoginSignup
3
0

More than 3 years have passed since last update.

CDKでGraphQLに挑戦する(lambda編)

Last updated at Posted at 2020-05-05

はじめに

今日はGWアドベントカレンダーの7日目です。今回は番外編ということでAppSyncからLambdaを呼び出す実装について書いていきます。

Lambdaを使いたい理由

本当は面倒な実装をやりたくないのですが、どうしても自分でLambdaを書かないといけない場合も存在するようです。このシリーズではItemという型のスキーマを最初に定義しましたが、例えばそこにcategoryという属性を足したいとします。最初に書いたスキーマを以下のように書き換えます。

enum Category {
    HOGEHOGE
    HUGAHUGA
}

type Item {
    id: String!
    data: String!
    category: Category!
}

type Query {
    allItem: [Item]
    allItemOnCategory(category: Category): [Item]
}

input ItemInput {
    data: String!
    category: Category!
}

type Mutation {
    addItem(item: ItemInput!): Item
}

Itemcategoryというenumで定義した値が追加されました。また、allItemOnCategoryを実行するとカテゴリ別でデータを取得させるようにスキーマを定義しました。

ここでidは必ず一意な値として必要なのでプライマリキーとして、categoryをソートキーとして定義します。ここまでは特に困ることはないのですが、このallItemOnCategoryのリゾルバーを実装するにはプライマリキーではなくソートキーから値を引っ張ってくる必要があるのですが、調べたところAppSyncのDynamoDBへのマッピングテンプレートは現在プライマリキーのみしかサポートされていないようです(わたしの勘違いかもしれないのでその場合はご指摘ください。。。)。
なので少し複雑なデータ操作をする場合は自分でLambdaを書くしかなさそうです。

テーブルの定義の拡張

categoryの値をプライマリキーのように扱いたいので今回はグローバルセカンダリインデックスをしようします。こんな感じでcdkを書き換えました。

const itemTable = new Table(this, 'itemTable', {
  partitionKey: {
    name: 'id',
    type: AttributeType.STRING,
  },
  sortKey: {
    name: 'category'
    type: AttributeType.STRING,
  }
})

itemTable.addGlobalSecondaryIndex({
  indexName: 'categoryKey',
  partitionKey: {
    name: 'category',
    type: AttributeType.STRING,
  }
})

Lambdaを呼び出す

GraphQLApiaddLambdaDataSourceというメソッドがサポートされているのでそれを用います。実装はこんな感じです。

const allItemOnCategory = new Function(this, 'allItemOnCategory', {
  code: Code.asset('lambda/allItemOnCategory'),
  handler: 'index.handler',
  runtime: Runtime.NODEJS_12_X,
  environment: {
    TABLE_NAME: itemTable.tableName,
  },
})
itemTable.grantReadData(allItemOnCategory)

api.addLambdaDataSource('ItemLambda', 'Item lambda source', allItemOnCategory).createResolver({
  typeName: 'Query',
  fieldName: 'allItemOnCategory',
  requestMappingTemplate: MappingTemplate.lambdaRequest(),
  responseMappingTemplate: MappingTemplate.lambdaResult(),
})

MappingTemplate.lambdaRequest()にはペイロードが指定できるみたいですが、クエリ変数とは別なので別段使う理由がなければ何も指定しなくて問題なさそうです。

Lambdaの実装

AppSyncからarguments: {...}という形でクエリ変数を受け取れるので、それを用いてデータの取得を実装します。

import { DynamoDB } from 'aws-sdk'

const dynamodb = new DynamoDB()

interface AllItemOnCategoryEvent {
  arguments: {
    category: string
  }
}

export const handler = async (event: AllItemOnCategoryEvent) => {

  const result = await dynamodb.query({
    TableName: process.env.TABLE_NAME!,
    ExpressionAttributeValues: {
      ':category': {
        S: event.arguments.category,
      },
    },
    KeyConditionExpression: 'category = :category',
    IndexName: 'categoryKey',
  }).promise()

  return result.Items?.map(item=>({
    id: item.id.S,
    data: item.data.S,
    category: item.data.S,
  }))
}

必要分のデータ(つまりは[Item]にマッチするデータ)さえ返せばクライアントとのリクエストやレスポンスのマッピングとかは勝手にAppSyncがやってくれるので何も考えずにデータのみを返します。

実装はこれだけです。API Gateway+Lambdaで書くよりも実装がシンプルになるのがいいですよね。

さいごに

全体のコードはこちらで公開しています。とりあえずベーシックでないDynamoDBの操作とかは、今の所自分でLambdaを組む必要があるみたいです(しかしもしかしたら勘違いしていることもあるかもしれないので知見のある方のご指摘を頂きたいです。。。)。

さて、7日間に渡ってAppSyncを追いかけてきました。そしてなんとかAppSyncというテーマで連日投稿してゴールをすることができました(そのせいで内容が薄くなった記事がほとんどでしたがw)。AppSyncの素晴らしさを少し学べたので今後もいかしていきたいなと思います。

明日は@nnhiguchiさんの投稿です。お楽しみに!

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