Open5

AWS AppSync

MaruMaru

AWS AppSyncはGraphQLを簡単に実装できるサービス
オフライン対応も可能にする(これはAmplify DataStoreが必要?)

GraphQL APIの構成

https://docs.aws.amazon.com/appsync/latest/devguide/api-components.html

GraphQL スキーマ

GraphQL スキーマはスキーマ定義言語(SDL)で記述される。(schema.graphqlschema.jsonで通常記述される)

SDLはTypeFieldで構成される

schema.graphqlの例

type Person {                                  
   id: ID! 
   name: String                                  
   age: Int
}
type Query {                                   
  people: [Person]
}
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}

id, name, age, people, addPersonがField

特別なTypeが存在する
Query
データの取得RESTでいうGET

Mutation
データの変更。RESTでいうPUT, POST

addPersonなどの操作を実現するためには、Resolverを用意する必要がある。

Resolverは対応するフィールドが呼ばれた時に実行されるコード群

MaruMaru

GraphQLの主要な型

inputsかoutputsかに分類できる

Objects

out

複数のフィールドを持つオブジェクト。

例: PersonとOccupationがObjects

type Person {
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

Scalars

in/out

Int, Float, String, Boolean, IDなどのプリミティブな型

他にもAWS独自のスカラー型がある
https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html

Inputs

in

フィールドをグループ化したもの

Special objects

特別な意味を持つType

query

RESTでいうGETリクエスト

type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
type Occupation {
  title: String
}
type Query {                                   
  people: [Person]
  occupations: [Occupation]
}

以下のように使う

query getItems {
   people {
      name
   }
   occupations {
      title
   }
}

output

{
  "data": {
    "people": [
      {
        "name": "John Smith"
      },
      {
        "name": "Andrew Miller"
      },
      .
      .
      .
    ],
    "occupations": [
      {
        "title": "Firefighter"
      },
      {
        "title": "Bookkeeper"
      },
      .
      .
      .
    ]
  }
}

mutation

RESTでいうPUT/POSTリクエスト

定義例

type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}

オブジェクトは直接引数に渡せないので、Inputを定義して渡す必要がある
Inputを引数に定義する例

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}

mutationの実行例

mutation createPerson {
  addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") {
    id
    name
    age
    occupation {
      title
    }
  }
}

レスポンス例

{
  "data": {
    "addPerson": {
      "id": "1",
      "name": "Steve Powers",
      "age": "50",
      "occupation": {
        "title": "Miner"
      }
    }
  }
}

subscription

WebSockerを使用するサーバーとクライアント間の双方向接続を開く

定義例

type Subscription {                                   
  personAdded: Person
}

subscription使用例

subscription personAddedOperation {
  personAdded {
    id
    name
  }
}

Personが追加された時のレスポンス例

{
  "data": {
    "personAdded": {
      "id": "1",
      "name": "Steve Powers"
    }
  }
}

Enums

in/out

有効な値のリスト

定義例

enum trafficSignals {
  solidRed
  solidYellow
  solidGreen
  greenArrowLeft
  ...
}

Unions/Interfaces

out

Unions
複数のTypeをどちらも返す可能性がある時に使う

Interfaces
Objectが実装する必要のあるフィールド群

https://docs.aws.amazon.com/appsync/latest/devguide/graphql-types.html
上記参照

MaruMaru

Field

Fieldとは

Typeの定義内に存在する、各値の定義のこと

以下の例でいうと、nameとageのこと

type Person {                                  
   name: String                                  
   age: Int
}

List

指定Typeの全てのアイテムを表す。[]で囲んで記述する

type Person { 
  name: String
  age: Int
}
type Query {                                   
  people: [Person] 
}

Non-nulls

nullにはできないフィールドを表す。Typeの後に! をつけて記述する

type Person { 
  name: String!
  age: Int
}
type Query {                                   
  people: [Person]
}
MaruMaru

データソース

データソースとは

AppSyncが操作するデータの参照先
一つのスキーマについて複数のデータソースを紐づけることができる。

データソース一覧

Amazon DynamoDB

フルマネージドNoSQL
AppSyncとの統合が一番しやすくなっている。

AWS Lambda

サーバレス関数
汎用性が高い

OpenSearch

全文検索エンジン

HTTP エンドポイント

HTTPエンドポイントをデータソースとして使用可能。
リゾルバでレスポンスの加工可能?

Amazon EventBridge

イベント駆動型アーキテクチャを実現できる

RDS

RDBをデータソースとしたいときに

None データソース

データソースが不要なときに設定する

MaruMaru

リゾルバー

リゾルバーとは?

出典: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-components.html

スキーマの各フィールドのデータについての処理を行うコード群のこと。
Query, Mutation, Subscriptionの操作を実装するために使われる。
スキーマとデータソースの中継をするイメージ?

リゾルバーランタイム

リゾルバーの実行環境(どの言語で書くか)

  • APPSYNC_JS for JavaScript
  • Velocity Template Language (VTL)

APPSYNC_JSが推奨とのこと

リゾルバーの構造

ユニットリゾルバーとパイプラインリゾルバーがある

ユニットリゾルバー

単一のリクエストハンドラーとレスポンスハンドラーを定義するコードで構成される。

リクエストハンドラー
コンテキストオブジェクトを引数として受け取り、データソースの呼び出しに使用されるリクエストペイロードを返す

レスポンスハンドラー
実行されたリクエストの結果を含むペイロードをデータソースから受け取り、
ペイロードをGraphQLレスポンスに変換して、GraphQL Fieldを解決する

パイプラインリゾルバー

リクエストハンドラーとレスポンスハンドラーのペアを「関数」とし、
複数の関数を順に実行できる。
複数関数ブロックの前後にリクエストハンドラー・レスポンスハンドラーを設定できる

リゾルバーハンドラーの構造

ハンドラーは以下のようなrequest(), response()と呼ばれる関数のこと。

export function request(ctx) {
    // Code goes here
}

export function response(ctx) {
    // Code goes here
}

例1 固定の文字列を返すQuery

以下のような定義があるとき

type Query {
   helloWorld: String!
}

helloWorldのリゾルバに以下のハンドラを設定すると、

export function request(ctx) {
    return {}
}

export function response(ctx) {
    return "Hello World"
}

以下のようなデータが常に返ってくるようになる

{
  "data": {
    "helloWorld": "Hello World"
  }
}

例2 DynamoDBデータソースからデータを取得するQuery

以下のような定義がある時、

type Book {
  id: ID!
  title: String
}

type Query {
  getBooks: [Book]
}

getBooksフィールドのリゾルバに以下のようなハンドラを設定する

/**
 * Performs a scan on the dynamodb data source
 */
export function request(ctx) {
  return { operation: 'Scan' };
}

/**
 * return a list of scanned post items
 */
export function response(ctx) {
  return ctx.result.items;
}

※リクエストハンドラで使われているScanはDynamoDBテーブルの全ての項目を取得する組み込みのOperation

上記のコード例のように、各ハンドラはctxというcontextオフジェクトを引数に受け取る。
リクエストハンドラーがreturnしたものはレスポンスハンドラーのctxに入ってくる。