Vue2(+ts)でCompositionAPIを導入済み前提。
ライブラリをインストール
npm i @vue/apollo-composable --save
main.ts で読み込む
import { provide } from '@vue/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client/core'
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
ApolloクライアントをVueにつなぐ(main.ts)
// Create an http link:
const httpLink = new HttpLink({
uri: "http://localhost:4000/"
});
// Cache implementation
const cache = new InMemoryCache({
typePolicies: {
Post:{
keyFields: ["id"],
},
Query:{
fields:{
posts:{
merge(existing, incoming) {
return incoming;
}
}
}
}
}
})
// Create a WebSocket link:
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true
}
});
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
(op) => {
const definition = getMainDefinition(op.query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
// client apollo client
// WebSocket 不要=subscription を使わないなら、link に
// GraphQLのサーバ文字列を渡すだけでよさそう。
const apolloClient = new ApolloClient({
link,
cache,
})
new Vue({
router,
store,
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: h => h(App)
}).$mount('#app')
取得
// import 部分
import { useQuery, useResult } from "@vue/apollo-composable";
// setup 抜粋
setup() {
const {result} = useQuery(ALL_POSTS); // ALL_POST は gql`~`
const posts = useResult(result, null, data => data.posts);
return {posts}
}
追加・更新・削除
// import 部分
import { useMutation, useResult } from "@vue/apollo-composable";
// setup 抜粋
setup() {
const createPost = ():void => {
mCreatePost({
title:post.title,
author:post.author,
});
}
const updatePost = ():void => {
mUpdatePost({
id:post.id,
title:post.title,
author:post.author,
});
}
const deletePost = (id:string):void => {
mDeletePost({
id
});
}
// mutate を mCreatePost, mUpdatePost,名前を変えて受け取り
const { mutate:mCreatePost } = useMutation(CREATE_POST,{ // CREATE_POST は gql`~`
// data:{ createPost } は mutation で指定しているキー名
update: (cache, { data: { createPost } }) => {
const data:{posts:Post[]}|null = cache.readQuery({ query: ALL_POSTS })
if(data !== null){
const ret:{posts:Post[]} = {posts:data.posts.concat([createPost])}
cache.writeQuery({ query: ALL_POSTS, data:ret })
}
},
});
const { mutate:mUpdatePost } = useMutation(UPDATE_POST,{ // UPDATE_POSTは gql`~`
// data:{ updatePost } は mutation で指定しているキー名
update: (cache, { data: { updatePost } }) => {
const data:{posts:Post[]}|null = cache.readQuery({ query: ALL_POSTS })
if(data !== null){
const ret:{posts:Post[]} = {posts:data.posts.reduce( (ret:Post[], post:Post):Post[]=>{
if(post.id === updatePost.id){
ret.push({
id:post.id,
title:updatePost.title,
author:updatePost.author
})
}else{
ret.push(post)
}
return ret;
},[])};
cache.writeQuery({ query: ALL_POSTS, data:ret })
}
},
});
const { mutate:mDeletePost } = useMutation(DELETE_POST,{ // DELETE_POSTは gql`~`
// data:{ createPost } は mutation で指定しているキー名
update: (cache, { data: { deletePost } }) => {
const data:{posts:Post[]}|null = cache.readQuery({ query: ALL_POSTS })
if(data !== null) {
const ret:{posts:Post[]} = {posts:data.posts.reduce( (ret:Post[], post:Post):Post[]=>{
if(post.id !== deletePost.id){
ret.push(post)
}
return ret;
},[])};
cache.writeQuery({ query: ALL_POSTS, data:ret })
}
},
});
return {createPost, updatePost, deletePost }
}
subscription
※ただし、データの反映を考えると、後述のsubscribeToMore の方が使い勝手がよさそうなので、ここの useSubscription をつかうことはあまりないのかも?…ということでデータがとれるところまでしか確認してない
// import 部分
import { useSubscription } from "@vue/apollo-composable";
// setup 抜粋
setup() {
const {result} = useSubscription(
SUBSCRIPTION_POST // SUBSCRIPTION_POST は gql`~`
);
watch(
() => result,
(data:any):void => {
if(data !== null){
if(data.value.post.mutation === 'DELETED'){
console.log(data.value.post.data.id, '削除');
}else if(data.value.post.mutation === 'UPDATED'){
console.log(data.value.post.data.id, '更新');
}else if(data.value.post.mutation === 'CREATED'){
console.log(data.value.post.data.id, '追加');
}
}
},
{
immediate:false,
deep:true
}
);
}
subscribeToMore
// setup 抜粋
setup() {
// まず、useQueryのところで subscribeToMore も受け取る
const {result, subscribeToMore} = useQuery(ALL_POSTS);
subscribeToMore(() => ({
document: SUBSCRIPTION_POST,
updateQuery: (previousResult, { subscriptionData }) => {
const ret:{posts:Post[]} = {posts:[].concat(previousResult.posts)};
if(subscriptionData.data.post.mutation === 'DELETED'){
console.log(subscriptionData.data.post.data.id, '削除');
const postIndex = ret.posts.findIndex((post) => post.id === subscriptionData.data.post.data.id);
if(postIndex !== -1){
ret.posts.splice(postIndex,1);
}
}else if(subscriptionData.data.post.mutation === 'UPDATED'){
console.log(subscriptionData.data.post.data.id, '更新');
ret.posts.forEach(post => {
if(post.id === subscriptionData.data.post.data.id){
post.title = subscriptionData.data.post.data.title;
post.author = subscriptionData.data.post.data.author;
}
})
}else if(subscriptionData.data.post.mutation === 'CREATED'){
console.log(subscriptionData.data.post.data.id, '追加');
ret.posts = previousResult.posts.concat([subscriptionData.data.post.data]);
}
return ret;
}
}))
// また、関連して、追加・更新・削除 のところで更新後の処理が不要になる
/* 中略 */
const { mutate:mCreatePost } = useMutation(CREATE_POST)
const { mutate:mUpdatePost } = useMutation(UPDATE_POST)
const { mutate:mDeletePost } = useMutation(DELETE_POST)
}