ReactからAppSyncへQueryを投げる
2018/10/15
Table of Contents
Queryを投げる準備
パッケージのインストール
ReactでAppSyncへ接続するために準備をしていきます。
まずは必要なパッケージをインストールします。
"aws-appsync": "^1.3.4",
"aws-appsync-react": "^1.1.4",
"graphql-tag": "^2.9.2",
"react-apollo": "^2.1.11",
package.jsonの中です。AppSyncではApolloを使っているのでApolloもインストールします。
GraphQLのリクエストはgraphql-tag
とreact-apollo
も直接使ってQueryやMutationもつかいます。
Provider
まずはApolloで使うコンポーネントをProvider
で囲みます。
const client = new AWSAppSyncClient({
・・・
});
class App extends Component {
render() {
return (
<ApolloProvider client={ client }>
<MainContainer />
</ApolloProvider>
);
}
}
これでMainContainer
内ではprops経由でApolloのリクエスト結果などにアクセスすることができます。
ReactでQueryを投げる
Queryを投げるコンポーネント
次にReactでQueryを投げてみたいと思います。さきほどのMainContainer
内でGraphQLのリクエストを投げます。
class ConditionsContainer extends React.Component {
constructor(props) {
super(props);
}
onEdit(c) {
this.props.history.push("/conditions/edit/" + c.user_id + "/" + c.date);
}
matchToCondition({ data : { listConditions: conditions }}) {
return conditions ? conditions.items : [];
}
render() {
return(
・・・
<ListConditions
conditions={ this.matchToCondition(this.props) }
onEdit={ this.onEdit.bind(this) }
/>
・・・
);
}
}
const mapStateToProps = (state) => ({
user: state.user
});
export default connect(
mapStateToProps,
)(compose(
graphql(ListConditionsQuery, {
options: (props) => {
const variable = { filter: {user_id: {eq: props.user.username}} };
return {
fetchPolicy: "cache-and-network",
variables: variable
}
},
props: ({ data }) => ({
data
})
})
)(ConditionsContainer));
ConditionContainer
を作ります。ここにAppSyncへのリクエストの結果などを扱えるようにします。connect
はreduxの関数です。コンポーネントをconnect
で囲むとreduxのstoreのデータにアクセスできます。storeにはユーザデータを入れるようにしています。ここで言うユーザデータはCognitoで認証したユーザデータをそのまま入れています。
compose
関数でGraphQLのリクエストの準備をし、スキーマ定義とリクエスト等でgraphql
関数を使います。
AppSyncで定義されているスキーマ
ここでgraphql
の説明に入る前に一度AppSyncとでで定義されているSchemaやDataSoucesについて触れておきます。
graphql
の第一引数にGraphQLのタグで作ったスキーマを入れます。こんな感じのものです。
import gql from 'graphql-tag';
export default gql`
query listConditions($filter: TableConditionFilterInput, $limit: Int, $nextToken: String) {
listConditions(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
user_id
date
physical_state
physical_note
work_state
work_note
leave_time
work_after
}
}
}
`;
gql
で囲ったところがスキーマの定義になります。ここからはGraphQLの話ですね。
query
なのでQueryを投げます。リクエストはlistConditions
で引数にfilter
、limit
、nextToken
をとるスキーマになります。ここらへんはAppSyncがデフォルトで提供してくれてるインターフェイスになります。(厳密に言うとAppSyncからDynamoDBへリクエストするResolverでリクエストできるようにするものです)
とくにfilter
はQueryでリクエストをするときにDynamoDBへ保存されているデータのフィルターに使われます。
filter
はこんな感じでAppSyncに定義されています。
input TableConditionFilterInput {
id: TableIDFilterInput
user_id: TableStringFilterInput
date: TableAWSDateFilterInput
physical_state: TableIntFilterInput
physical_note: TableStringFilterInput
work_state: TableIntFilterInput
work_note: TableStringFilterInput
work_after: TableStringFilterInput
leave_time: TableStringFilterInput
}
DynamoDBの各カラムごとにリクエストで検索条件を定義できます。一つピックアップしてdate
のTableAWSDateFilterInput
の中を見てみましょう。
input TableAWSDateFilterInput {
ne: AWSDate
eq: AWSDate
le: AWSDate
lt: AWSDate
ge: AWSDate
gt: AWSDate
contains: AWSDate
notContains: AWSDate
between: [AWSDate]
}
それぞれ検索条件でたとえばne
ですとnot equalなので等しくないときの条件ですね。
ここらへんは関係演算子(比較演算子かな)のと一緒です。betweenなんかでも検索できたりと大抵の検索条件は整っています。
これか各カラムにあるのですべてのカラムに対して検索条件を決めれるっということです。
当たり前だと思うかもしれませんが割と重要なことでGraphQLはあくまでもクライアントとサーバ間でのインターフェイスのみを定義しており(いわゆるスキーマ)検索方法、条件の細かい設定はサーバ側で実装しないといけません。
これをAppSyncはデフォルトでサポートしてるのでとてもうれしいですね。
このリクエストをAppSyncが受け取ったらDynamoDBへリクエストするんですが、その時のマッピング情報を定義してあげる必要があります。
{
"version": "2017-02-28",
"operation": "Scan",
"filter": #if($context.args.filter) $util.transform.toDynamoDBFilterExpression($ctx.args.filter) #else null #end,
"limit": $util.defaultIfNull($ctx.args.limit, 100),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
}
このような形で定義してあげます。先程のfilter
内のリクエスト条件は$ctx.args.filter
に入っており、 $util.transform.toDynamoDBFilterExpression
関数に渡すことで検索条件通りにリクエストを変換してくれます。
Reactでのリクエスト結果
AppSyncで定義されているスキーマを確認しましたので、Reactに戻って実際のリクエストを投げるところを見てみたいと思います。
compose
関数で囲まれた中にgraphql
でスキーマごとにリクエストするときの引数の設定、リクエスト結果の処理などを決めていきます。
graphql(ListConditionsQuery, {
options: (props) => {
const variable = { filter: {user_id: {eq: props.user.username}} };
return {
fetchPolicy: "cache-and-network",
variables: variable
}
},
props: ({ data }) => ({
data
})
})
ListConditionsQuery
は先程見ましたね。GraphQLのスキーマになります。
次にoptions
でリクエストするときの引数を渡します。
options
の引数にはConditionsContainer
のpropsへアクセスすることができます。これでreduxのstoreに入っているユーザデータにアクセスします。ここではユーザの名前を取得しています。
次に引数となるvariables
にはユーザデータのユーザ名で検索するように検索条件を指定します。
それをoptions
の返り値としてvariables
に入れることでListConditionsQuery
スキーマの引数として渡すことができます。
最後にprops
っとありますが、これはリクエストした結果をコンポーネントへ渡すところになります。引数として検索結果が返ってきておりdata
の中に検索結果のデータが入っています。
リザルト
これでQueryを投げて結果が返ってくるところまでできました。後はQueryの結果をコンポーネントに渡して画面に表示させれば一通りのQuery処理の完了となります。
matchToCondition({ data : { listConditions: conditions }}) {
return conditions ? conditions.items : [];
}
render() {
return(
・・・
<ListConditions
conditions={ this.matchToCondition(this.props) }
matchToCondition
の引数でコンディションリストへ一発でアクセスするよう引数に階層をつけてコンポーネントのprops
を渡します。data
は上記のgraphql
関数内でprops
関数で返り値として渡したdata
になります。data
内に入っているオブジェクトのlistConditions
キーで指定しているものへアクセスしています。conditions
がある場合はそのまま返し、ない場合はからのリストを返すようにしています。これでListConditions
コンポーネントには確実に配列のオブジェクトを渡すことできるようになります。
まとめ
- 今回はReactからAppSyncへQueryを投げるときの実装を見ていきました。クライアント側は簡単で、GraphQLのスキーマを定義し、Queryを投げ、結果を画面に描画するだけでした。
- サーバ側ではQueryの検索条件を実装せずにAppSync + DynamoDBですべて解決できほぼほぼ手を加えることなくサーバ側の実装もすることができかなり楽でした(これを自前で組もうとするとマジしんどいです、、)
- AppSyncからDynamoDBへのマッピング処理ですかさらっと流しましたが、実はここの処理が大事でAppSyncからどのような形でDynamoDBへリクエストを投げるかを定義している部分になります。ここを深く知っていればもっと自由度高く検索することができるのではないでしょうか。
- またMutationのときには違う実装となっていますのでそのときに触れればと思います。
kobaru
インフラ好きっ子