AppSync ことはじめ

入門 1: AppSync で Hello, World!

まず AppSync で “Hello, World!” という文字列を返す単純な GraphQL API を作成し、サービスの利用法をみていきます

スキーマの作成

まずは GraphQL SDL(Schema Definition Language) を用いて GraphQL API のインターフェースを定義していきます。次のような単純な User データを返却する user クエリを定義します。

schema {
  query: Query
}

type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
}

データソース/リゾルバの作成

ことはじめとして、決まった値を返すようなリゾルバを定義します。

{ "id": 1, "name": "hoge" }

データソースは None タイプを選択したものを指定し、レスポンスマッピングテンプレートにて前述の定数値を返すように設定します。

クエリの実行

次のようなクエリを実行します。定数値を返すレスポンスマッピングテンプレートを設定しているので id を変えても常に同じ値が帰ります。name のみにすると、戻り値に id は含まれなくなります。

query {
  user(id: 1) {
    id
    name
  }
}

SAM テンプレート

CloudFormation テンプレートで以上のリソースを表現しておくとわかりやすいかもしれません。次のようになります。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  Hello AppSync

Resources:
  GraphQLApi:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      Name: GraphQLHello
      AuthenticationType: API_KEY
  GraphQLSchema:
    Type: AWS::AppSync::GraphQLSchema
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      DefinitionS3Location: schema.graphql
  GraphQLNoneDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      Name: GraphQLNoneDataSource
      ApiId: !GetAtt GraphQLApi.ApiId
      Type: NONE
  GraphQLResolver:
    Type: AWS::AppSync::Resolver
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      TypeName: Query
      FieldName: user
      DataSourceName: !GetAtt GraphQLNoneDataSource.Name
      RequestMappingTemplate: '{"version": "2017-02-28", "payload": {}}'
      ResponseMappingTemplate: '{"id":1, "name":"hoge"}'
  GraphQLAPIKey:
    Type: AWS::AppSync::ApiKey
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId

入門 2: 値を Lambda データソースから返却する

定数値をレスポンスマッピングテンプレートから返却するのではなく、Lambda 関数から返してみます。

データソースの作成

以下のような単純な Lambda 関数を作成し、これをデータソースとして指定します。

import json


def lambda_handler(event, context):
    print(json.dumps(event, ensure_ascii=False))
    return {
        'id': event['id'],
        'name': 'hogefuga'
    }

リゾルバの作成

リゾルバのマッピングテンプレートを以下のように設定すると背後にある Lambda 関数をデータソースとして呼び出せます

# request
{ "version" : "2017-02-28", "operation": "Invoke", "payload": $util.toJson($context.args) }

# response
$util.toJson($context.result)

クエリの実行

以下のようなクエリを実行すると無事 Lambda から返却された id に対応するレスポンスが得られます(name は固定値ですが…)。

query {
  user(id: 123) {
    id
    name
  }
}

あわせて Lambda 関数の CloudWatch Logs をみると以下のようなイベントが渡っていることがわかります

{
  'id': '123'
}

SAM テンプレート

Lambda 関数が絡んできたので SAM が生きます。テンプレートは以下のようなもの。AppSync から Lambda を実行する際の実行ロールがやや冗長なので、スマートにかけるようになると便利そうですね。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  Hello AppSync

Resources:
  GraphQLApi:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      Name: GraphQLHello
      AuthenticationType: API_KEY
  GraphQLSchema:
    Type: AWS::AppSync::GraphQLSchema
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      DefinitionS3Location: schema.graphql
  GraphQLDataSourceLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: get_user/
      Handler: app.lambda_handler
      Runtime: python3.8
  AppSyncLambdaInvokeRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: appsync.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: AllowInvokeLambda
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: lambda:invokeFunction
                Resource: !GetAtt GraphQLDataSourceLambdaFunction.Arn
  GraphQLLambdaDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      Name: GraphQLLambdaDataSource
      ApiId: !GetAtt GraphQLApi.ApiId
      Type: AWS_LAMBDA
      LambdaConfig:
        LambdaFunctionArn: !GetAtt GraphQLDataSourceLambdaFunction.Arn
      ServiceRoleArn: !GetAtt AppSyncLambdaInvokeRole.Arn
  GraphQLResolver:
    Type: AWS::AppSync::Resolver
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      TypeName: Query
      FieldName: user
      DataSourceName: !GetAtt GraphQLLambdaDataSource.Name
      RequestMappingTemplate: '{ "version" : "2017-02-28", "operation": "Invoke", "payload": $util.toJson($context.args) }'
      ResponseMappingTemplate: "$util.toJson($context.result)"
  GraphQLAPIKey:
    Type: AWS::AppSync::ApiKey
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId

入門 3: HTTP データソースの利用

  • スキーマは以前と同様にデータソースを HTTP データソースに変更してみる
  • 簡単のため JSONPlaceholder の Users API を叩く

データソースの作成

JSONPlaceholder のエンドポイント: https://jsonplaceholder.typicode.com を指定し、データソースを作成する

リゾルバの作成

リゾルバのマッピングテンプレートを以下のように設定すると背後にある HTTP エンドポイントをデータソースとして呼び出せる

# request
{"version":"2018-05-29","method":"GET","resourcePath":"/users/$ctx.args.id"}

# response
$context.result.body

クエリの実行

以下のようなクエリを実行する

query {
  get(id: "3") {
    id
    name
  }
}

すると以下のような結果が得られる

{
  "data": {
    "get": {
      "id": "3",
      "name": "Clementine Bauch"
    }
  }
}

SAM テンプレート

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  Hello AppSync

Resources:
  GraphQLApi:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      Name: GraphQLHello
      AuthenticationType: API_KEY
  GraphQLSchema:
    Type: AWS::AppSync::GraphQLSchema
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      DefinitionS3Location: schema.graphql
  GraphQLAPIKey:
    Type: AWS::AppSync::ApiKey
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
  GraphQLHTTPDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      Name: GraphQLHTTPDataSource
      ApiId: !GetAtt GraphQLApi.ApiId
      Type: HTTP
      HttpConfig:
        Endpoint: https://jsonplaceholder.typicode.com
  GraphQLResolver:
    Type: AWS::AppSync::Resolver
    Properties:
      ApiId: !GetAtt GraphQLApi.ApiId
      TypeName: Query
      FieldName: user
      DataSourceName: !GetAtt GraphQLHTTPDataSource.Name
      RequestMappingTemplate: '{"version":"2018-05-29","method":"GET","resourcePath":"/users/$ctx.args.id"}'
      ResponseMappingTemplate: "$context.result.body"

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください