Daiki Urata (Fusic Co.,Ltd.)
Twitter: @daiki7nohe
GraphQLについて
Apolloについて
Apollo ClientのCache
Vue + GraphQLのLocal State Management
API用のクエリ言語
一つのエンドポイント
型がある
受け取り側でスキーマを定義
要求側でQueryを投げてデータ取得
サーバーへの変更はMutation
type Query {
todos: [Todo]
}
type Post {
id: ID!
title: String
body: String
}
query GetPosts {
posts {
id
title
}
}
mutation CreateTodo ($title: String, $body: String) {
createTodo (title: $title, body: $body) {
id
}
}
GraphQLのプラットフォーム
Apollo Server, Apollo Clientなどのオープンソース
Schema registryなどのクラウドサービス
その他IDEプラグインやブラウザ拡張ツールなど
GraphQLクライアント
react-apollo, apollo-angular, vue-apollo
キャッシュされる
Vue CLIならvue-cli-plugin-apolloが簡単
// apollo.js
import { ApolloClient } from "apollo-client";
const apolloClient = new ApolloClient({ ... })
export default apolloClient;
// main.js
import VueApollo from "vue-apollo";
import apolloClient from "./apollo";
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
new Vue({
apolloProvider,
render: h => h(App)
}).$mount("#app");
vue-apollo (apolloオプション)
import gql from "graphql-tag";
export default {
apollo: {
todos: {
query: gql`
query getTodos {
todos {
id
title
body
}
}
`
}
},
data () {
return {
todos: []
}
}
}
vue-apollo ($apollo)
export default {
created () {
this.$apollo.query({
query: gql`
query getTodos {
todos {
id
title
body
}
}
`
})
.then(result => {
// Your code here...
})
}
}
vue-apollo (ApolloQueryコンポーネント)
<ApolloQuery
:query="require('../graphql/GetTodos.gql')"
>
<template slot-scope="{ result: { loading, error, data } }">
<!-- Loading -->
<div v-if="loading">Loading...</div>
<!-- Error -->
<div v-else-if="error">An error occured</div>
<!-- Result -->
<ul v-else-if="data">
<li v-for="todo in data.todos" :key="todo.id">
Title: {{ todo.title }}, Body: {{ todo.body }}
</li>
</ul>
<!-- No result -->
<div v-else>No result :(</div>
</template>
</ApolloQuery>
一度投げたクエリ結果はキャッシュされる
クエリに変更があればサーバーへリクエスト
fetchPolicyで変えられる
$ npm install apollo-cache-inmemory
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { ApolloClient } from 'apollo-client';
const cache = new InMemoryCache();
const client = new ApolloClient({
link: new HttpLink(),
cache
});
fetchPolicy
cache-first
: クエリ結果をキャッシュし、その後はキャッシュを使う
cache-and-network
: キャッシュも使いつつ、サーバーへはリクエスト
network-only
: キャッシュなし。必ずサーバーへリクエスト
cache-only
: キャッシュだけにアクセス
no-cache
: network-only
とほぼ同じだが、クエリ結果すらキャッシュに残さない
ある程度大規模になるとVuexで状態管理したい
キャッシュデータをまたVuexで?
Vuexからキャッシュへわざわざアクセス?
Apolloのキャッシュでローカルの状態も一括管理
元々は apollo-link-state
(今はdeprecated)
Apollo Client >= 2.5ではそのままでOK
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
const cache = new InMemoryCache();
const client = new ApolloClient({
cache,
resolvers: {
Mutation: {
setSelectedId(_root, { id }, { cache }) {
cache.writeData({ data: { selectedId: id } });
return null;
}
}
},
});
// 初期値をセット
cache.writeData({
data: {
selectedId: 1
}
});
this.$apollo.mutate({
mutation: gql`
mutation SetSelectedId($id: ID) {
setSelectedId(id: $id) @client
}
`,
variables: {
id
}
});
this.$apollo.getClient().writeQuery({
query: gql`
{
selectedId
}
`,
data: {
selectedId: id
}
});
export default {
apollo: {
selectedId: {
query: gql`
query GetSelectedID {
selectedId @client
}
`
}
},
data() {
return {
selectedId: null
};
},
}
this.$apollo.query({
query: gql`
query GetSelectedID {
selectedId @client
}
`
}).then(result => {
this.todos = result.data.todos
});
this.todos = this.$apollo.getClient().readQuery({
query: gql`
query GetSelectedID {
selectedId @client
}
`
});
Local Stateのスキーマは定義できるがバリデーションまではしてくれない
new ApolloClient({
typeDefs: gql`
extend type Query {
selectedId: Boolean! // <- エラーにならない
}
`
})
Apolloキャッシュ便利
ただ状態管理は色々と考える部分あり