AWS AppSync
AWS AppSyncはGraphQLを簡単に実装できるサービス
オフライン対応も可能にする(これはAmplify DataStoreが必要?)
GraphQL APIの構成
GraphQL スキーマ
GraphQL スキーマはスキーマ定義言語(SDL)で記述される。(schema.graphql
かschema.json
で通常記述される)
SDLはTypeとFieldで構成される
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は対応するフィールドが呼ばれた時に実行されるコード群
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独自のスカラー型がある
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が実装する必要のあるフィールド群
上記参照
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]
}
データソース
データソースとは
AppSyncが操作するデータの参照先
一つのスキーマについて複数のデータソースを紐づけることができる。
データソース一覧
Amazon DynamoDB
フルマネージドNoSQL
AppSyncとの統合が一番しやすくなっている。
AWS Lambda
サーバレス関数
汎用性が高い
OpenSearch
全文検索エンジン
HTTP エンドポイント
HTTPエンドポイントをデータソースとして使用可能。
リゾルバでレスポンスの加工可能?
Amazon EventBridge
イベント駆動型アーキテクチャを実現できる
RDS
RDBをデータソースとしたいときに
None データソース
データソースが不要なときに設定する
リゾルバー
リゾルバーとは?
出典: 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に入ってくる。