4
4

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 3 years have passed since last update.

Terraform で構築した AWS AppSync で GraphQL を触る

Last updated at Posted at 2020-07-25

概要

  • Serverless 界隈でよく使われる GraphQL を触ってみた
  • Backend を用意するのが面倒なので AWS AppSync を採用
  • 簡単に構築できるように Terraform で書いた

サンプルコード

以下リポジトリを clone するとすぐに terraform で環境構築できます。
https://github.com/tsubasaogawa/terraform-appsync-graphql-test

Terraform v0.12.24 で動作確認

おさらい

GraphQL

  • API によく使われる言語
  • REST API と比べ
    • リクエストの回数を減らしやすい (一度に複数の機能を呼び出せる)
    • レスポンスされる項目を自由に指定できる
  • スキーマ第一主義
    • フロントエンドとバックエンドの認識齟齬を防ぐ

AppSync

  • GraphQL の API サーバーをマネージドで提供してくれるサービス
    • REST における API Gateway の GraphQL 版のようなもの
  • データソース (API のデータ読み取り先) として AWS の各サービス (Dynamo/Elasticsearch/Lambda etc.) や HTTP が選択できる
    • ノンプログラミングで DB からデータを読み書きする API が作れる

作るもの

  • AppSync (GraphQL API)
    • Query (SQL の SELECT に相当)
      • user: ユーザー情報を DB から取得
    • Mutation (SQL の UPDATE/INSERT/DELETE に相当)
      • createUser: ユーザー情報を DB に書き込み
  • DynamoDB
    • user の保存先

image.png

コード抜粋

ここでは AppSync の tf ファイルを掲載。variables は別ファイルで定義されている。
必要となる resource は以下の 4 つ。

  • aws_appsync_graphql_api
  • aws_appsync_api_key
  • aws_appsync_datasource
  • aws_appsync_resolver
modules/appsync/main.tf
# schema は別ファイルで定義しておく
data "local_file" "graphql_schema" {
  filename = "modules/appsync/resources/schema.graphql"
}

resource "aws_appsync_graphql_api" "this" {
  name                = var.name
  authentication_type = "API_KEY"
  schema              = data.local_file.graphql_schema.content
}

resource "aws_appsync_api_key" "this" {
  api_id = aws_appsync_graphql_api.this.id
}

resource "aws_appsync_datasource" "this" {
  api_id           = aws_appsync_graphql_api.this.id
  name             = var.datasource_name[
  # IAM Role も別ファイルで定義
  service_role_arn = aws_iam_role.this.arn
  type             = "AMAZON_DYNAMODB"

  dynamodb_config {
    table_name = var.dynamo_table_name
  }
}

# field の数だけ resolver を定義できるように map 型の resolvers 変数を使う
# https://github.com/tsubasaogawa/terraform-appsync-graphql-test/blob/c112fbd649ef80aeae2648a1fab21ff0bf64af6d/locals.tf#L13-L24
data "local_file" "resolver_requests" {
  for_each = var.resolvers
  filename = "modules/appsync/resources/resolver_templates/${each.key}/request.template"
}

data "local_file" "resolver_responses" {
  for_each = var.resolvers
  filename = "modules/appsync/resources/resolver_templates/${each.key}/response.template"
}

resource "aws_appsync_resolver" "this" {
  for_each    = var.resolvers
  api_id      = aws_appsync_graphql_api.this.id
  field       = lookup(each.value, "field")
  type        = lookup(each.value, "type")
  data_source = lookup(each.value, "data_source")

  request_template  = lookup(data.local_file.resolver_requests, each.key).content
  response_template = lookup(data.local_file.resolver_responses, each.key).content
}

実行

環境構築

# Setup
cd terraform-appsync-graphql-test
terraform init
terraform apply

成功すると AppSync のマネジメントコンソールで API が作成されていることが確認できる。

image.png

API テスト

下準備

ここでは作成された GraphQL API を curl で叩いてみる。
curl するために必要な API KEY とホスト名を terraform の output から取得する。

# Set environment variables from tfstate
API_KEY=$(cat terraform.tfstate | jq -r '.outputs.appsync_api_key.value')
HOST=$(cat terraform.tfstate | jq -r '.outputs.appsync_api_uris.value.GRAPHQL' | grep -oP '[^/]+\.amazonaws.com')

createUser でユーザーを作成

request
curl \
  -H 'Content-Type: application/json' \
  -H "x-api-key: $API_KEY" \
  -H "Host: $HOST" \
  -X POST -d '
    {
      "query": "mutation { createUser(name: \"yoshida\") { id name } }"
    }' \
  https://$HOST/graphql
response
{"data":{"createUser":{"id":"2e591d0b-c4cd-41bd-b66a-ad637e506f5f","name":"yoshida"}}}

yoshida というユーザーが 2e591... という ID で作成されたことがわかる。

user でユーザー情報を取得

先程作成した yoshida さんの情報を DB から取得する。ここでは name 属性のみを取得する。

request
curl \
  -H 'Content-Type: application/json' \
  -H "x-api-key: $API_KEY" \
  -H "Host: $HOST" \
  -X POST -d '
    {
      "query": "query { user(id: \"2e591d0b-c4cd-41bd-b66a-ad637e506f5f\") { name } }"
    }' \
  https://$HOST/graphql
response
{"data":{"user":{"name":"yoshida"}}}

必要な項目を API の利用者側が選択できるため、データのやり取りが軽量で済む。

備考/所感

AppSync の Resolver

AWS AppSync 特有の機能が Resolver であり、GraphQL の Field (本頁における user/createUser) のロジックを表現する。Request と Response を別々の mapping template ファイルとして定義する。
例えば user の Resolver は以下のように定義されている。

  • Request: DynamoDB に GetItem を発行する。
  • Response: 得られた結果を JSON で返す。

Mapping template のリファレンスは以下にまとめられている。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/resolver-mapping-template-reference.html

学習コスト

上記の通り Resolver の考え方が若干複雑なのと、GraphQL の Schema における記法がこれまた独特であることから、REST API と比べると若干の取っ付きにくさ・学習コストの高さを感じる。ちょっとした API であれば素直に API Gateway を使うのが手っ取り早い。

※ なお、API Gateway であっても Service Proxy を用いることで Lambda レスで DynamoDB などの AWS リソースを扱うことができる。
https://dev.classmethod.jp/articles/api-gateway-aws-service-proxy-dynamodb/

一方、比較的大きい (育ちそうな) API であれば GraphQL のメリットをしっかり享受できるため、利用を検討してもいいかもしれない。

4
4
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
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?