Vue ApolloでGraphQLを使ったデータ取得の基本ガイド
はじめに
現代のWebアプリケーションでは、効率的なデータ通信が求められています。その中で、GraphQLは柔軟なデータ取得方法として注目を集めています。
この記事では、Vue.jsとApollo Clientを用いて、GraphQLクエリを簡単に実装する方法を解説します。
公式ドキュメント以外でVue Apolloのセットアップ方法を詳しく説明している記事が少なかったため、 自分用のメモとしても、プロジェクトのセットアップ手順を詳しく説明したいと思います。
Vue Apolloとは
Vue Apolloは、Vue.jsとApollo Clientを組み合わせ、GraphQLを簡単に扱えるようにするライブラリです。これにより、Vue.jsアプリケーションで効率的にGraphQLを利用でき、データのやり取りがシンプルになります。
さらに、データの取得やキャッシュ管理、リアルタイム更新などを簡単に実装できます。
プロジェクトのセットアップ
Vue.jsでApollo Clientを使用するために、まず必要なパッケージをインストールし、Apollo Clientを設定します。
これを段階的に行い、それぞれのステップを詳しく解説します。
1. パッケージのインストール
まず、Vue.jsでApollo Clientを使うために必要なパッケージをインストールします。
以下のコマンドを実行してください。
npm install @apollo/client graphql @vue/apollo-composable
もしくは
yarn add @vue/apollo-composable
-
@apollo/client
は、Apollo Client本体です。GraphQLのクエリやミューテーション(データの変更操作)を実行するために必要です。 -
graphql
は、GraphQLのスキーマやクエリ構造を解釈するためのライブラリです。 -
@vue/apollo-composable
は、Vue.jsでApollo Clientを使いやすくするためのVue用のラッパーです。Vueのsetup()関数内で、直感的にApolloを扱えるようになります。
2. Apollo Clientのセットアップ
次に、プロジェクト全体でApollo Clientを使用できるように設定するため、main.jsファイルに以下のコードを追加します。
import "./assets/main.css";
import { createApp } from 'vue'
import App from "./App.vue";
import router from "./router";
+ import { createHttpLink } from "@apollo/client/core";
+ const httpLink = createHttpLink({
+ uri: 'http://localhost:3020/graphql',
+ })
const app = createApp(App)
app.use(router);
app.mount("#app");
-
createHttpLink
は、GraphQLサーバーへのリクエストを送信するためのリンクを作成する関数です。 -
uri
にはgraphqlエンドポイントのURIを指定します。
次に以下のコードを追加します。
import "./assets/main.css";
import { createApp } from 'vue'
import App from "./App.vue";
import router from "./router";
+ import { createHttpLink, InMemoryCache } from "@apollo/client/core";
const httpLink = createHttpLink({
uri: 'http://localhost:8000/graphql',
})
+ const cache = new InMemoryCache();
const app = createApp(App)
app.use(router);
app.mount("#app");
-
InMemoryCache
は、GraphQLクエリやミューテーションから取得したデータを、メモリ上にキャッシュするための機能です。 - 取得したデータが再利用可能になり、サーバーへの不要なリクエストを減らしてパフォーマンスの向上につながります。
- また、デフォルトでGraphQLのオブジェクトに含まれる
__typenameフィールド
と一意のフィールド(idや_id)を組み合わせてキャッシュキーを作成し、データが更新された際に、そのキーに基づいて変更された情報が自動的にキャッシュに反映されます。
次に以下のコードを追加します。
import "./assets/main.css";
import { createApp } from 'vue'
import App from "./App.vue";
import router from "./router";
+ import { createHttpLink, InMemoryCache, ApolloClient } from "@apollo/client/core";
const httpLink = createHttpLink({
uri: 'http://localhost:8000/graphql',
})
const cache = new InMemoryCache();
+ const apolloClient = new ApolloClient({
+ link: httpLink,
+ cache,
+ });
const app = createApp(App)
app.use(router);
app.mount("#app");
- ここで実際のApollo Clientを作成し、先ほどGraphQLサーバーのエンドポイントを設定した
httpLink
とcache
(キャッシュ機能)を渡します。
最後に以下のコードを追加します。
import "./assets/main.css";
+ import { createApp, provide, h } from "vue";
+ import { DefaultApolloClient } from "@vue/apollo-composable";
import App from "./App.vue";
import router from "./router";
import { createHttpLink, InMemoryCache, ApolloClient } from "@apollo/client/core";
const httpLink = createHttpLink({
uri: 'http://localhost:8000/graphql',
})
const cache = new InMemoryCache();
const apolloClient = new ApolloClient({
link: httpLink,
cache,
});
- const app = createApp(App)
+ const app = createApp({
+ setup() {
+ provide(DefaultApolloClient, apolloClient);
+ },
+ render: () => h(App),
+ });
app.use(router);
app.mount("#app");
-
provide関数
を使用してapolloClient
のインスタンスをアプリケーションの全コンポーネントで使用できるようにしています。 -
DefaultApolloClient
は、Apollo Clientをデフォルトのクライアントとして提供するために使用されます。 -
h(hyperscript)関数
を使い、Appコンポーネントをレンダリングしています。
GraphQLクエリを実行する
セットアップが完了したので、実際にGraphQLクエリを使ってデータを取得・更新をしてみたいと思います!
1. useQuery
useQueryフックは、Apollo ClientでGraphQLクエリを実行し、その結果をリアクティブに管理するための手段です。
<script setup>
import { useQuery } from "@vue/apollo-composable";
import { gql } from "graphql-tag";
const GET_POST = gql`
query GetPosts {
getPosts {
id
postId
title
text
}
}
`;
const { result, loading, error } = useQuery(GET_POST);
</script>
<template>
<div v-if="loading">Loading...</div>
<ul v-else-if="result && result.posts">
<li v-for="post of result.posts" :key="post.id">
{{ post.title }} : {{ post.text }}
</li>
</ul>
</template>
-
gql
はApollo Clientが提供するタグ付きテンプレートリテラルです。gql
タグをつけることで、クエリ文がパースされ、Apollo Clientが理解できるクエリとして処理されます。 -
getPosts
がGraphQLサーバー側にあるクエリ名で、id
,postId
,title
,text
はフィールドと呼ばれ、リクエストした際にこれらの値がサーバーからレスポンスされます。 -
result
はクエリの結果データです。ここでは、ユーザー一覧が含まれます。resultのデータはリアクティブで、クエリが再実行されたりデータが変更された場合に自動で更新されます。 -
loading
はクエリの実行中であることを示すbool値が入ります。この値がtrueの場合、データはまだ取得されていないことを示します。通常は、ロード中にスピナーや「Loading...」の表示などを行います。 -
error
にはクエリの実行中に発生したエラー情報が入ります。エラーが発生した場合、このオブジェクトにエラーメッセージや詳細情報が含まれます。
上記の例では、特定のパラメータなしでクエリを実行しましたが、動的にパラメータを渡してクエリを実行することも可能です。
たとえば、ユーザーIDを指定して特定のユーザー情報を取得する場合、以下のようにクエリを変更できます。
<script setup>
import { useQuery } from '@vue/apollo-composable';
import gql from 'graphql-tag';
import { ref } from 'vue';
// 取得したいユーザーIDを指定
const postId = ref(1);
// パラメータ付きクエリ
const GET_POST = gql`
query GetPost($id: ID!) {
getPost(id: $id) {
id
postId
title
text
}
}
`;
// useQueryにパラメータを渡してクエリを実行
const { result, loading, error } = useQuery(GET_POST, { id: postId });
</script>
2. useMutation
useQuery
がデータの取得を行うのに対し、useMutation
はデータの作成・更新・削除といった操作を行います。
<script setup>
import { ref } from "vue";
import { useMutation } from "@vue/apollo-composable";
import { gql } from "graphql-tag";
import { GET_POST } from "../graphql/queries/getAllPosts";
// 投稿者のID
const userId = ref(1);
const newTitle = ref("");
const newMessage = ref("");
const { mutate: createPost } = useMutation(
gql`
mutation createPost($input: createPostInput!) {
createPost(input: $input) {
id
title
text
}
}
`,
() => ({
variables: {
input: {
userId: userId.value, // ユーザーIDを設定
title: newTitle.value, // タイトルを設定
text: newMessage.value, // メッセージを設定
},
},
// クエリの際実行
refetchQueries: [{ query: GET_POST }],
// refetchQueriesが完了するまで待機
awaitRefetchQueries: true,
})
);
</script>
<template>
<div>
<p>タイトル</p>
<input type="text" v-model="newTitle" />
<p>本文</p>
<textarea
cols="30"
rows="10"
v-model="newMessage"
placeholder="Enter a message"
></textarea>
<button @click="createPost()">投稿</button>
</div>
</template>
上記のコードを分解しながら説明していこうと思います。
まず初めに、useMutation
を使用して、gql
タグの中に新規投稿作成に必要なミューテーションを定義します。
gql`
mutation createPost($input: createPostInput!) {
createPost(input: $input) {
id
title
text
}
}
`,
-
createPost
はGraphQLサーバー側で定義されているミューテーションの名前です。これを呼び出すことで、サーバー側で新しい投稿が作成されます。 - ミューテーションの引数として
$input
が指定されています。$input
には、新規投稿のデータを含むオブジェクトが含まれます。これはcreatePostInput!型
で定義されているため、この形式でデータを渡す必要があります。 - ミューテーションが成功した場合、サーバーからレスポンスとして新規に作成された投稿のデータ(
id
,title
,text
)が返されます。
次にvariables
を使用してcreatePost
の引数にinput型
の値を渡すようにします。
() => ({
variables: {
input: {
userId: userId.value,
title: newTitle.value,
text: newMessage.value,
}
}
})
-
variables
は、クエリやミューテーションに対して、パラメータを動的に渡すためのオブジェクトです。 -
variables
を使用して、input
オブジェクトの中にuserId
、title
、text
などの必要な情報を含めて、ミューテーションの引数としてサーバーに渡しています。
次にrefetchQueries
とawaitRefetchQueries
の役割について説明します。
// クエリの際実行
refetchQueries: [{ query: GET_POST }],
// refetchQueriesが完了するまで待機
awaitRefetchQueries: true,
-
refetchQueries
は、ミューテーションが成功した後に、特定のクエリを再実行(リフェッチ)するために使います。指定されたクエリ(この場合はGET_POST
)を再度実行し、サーバーから最新のデータを取得します。 -
awaitRefetchQueries: true
はrefetchQueries
で指定したクエリのリフェッチが完了するまで待機するためのオプションです。 - ミューテーションが完了してすぐに次の処理に進むのではなく、リフェッチされるクエリ(ここでは
GET_POST
)が完了するまで待機します。これにより、最新のデータを正確に取得し、その後の処理を行うことができます。 - この2つの設定によって、ミューテーション後のデータ整合性を保ちながら、クライアント側に最新の情報が正確に反映されるようにしています。
おまけ
GraphQLミューテーションを実行する際、操作の成功や失敗に応じて適切な処理を行いたい時があると思います。その際に、Vue Apolloでは、useMutation
を使用する際に、onDone
とonError
のコールバックを活用することで、これらの状況に対応することができます!(便利ですね👏)
onDone
ミューテーションが成功完了した際に呼び出されるコールバック関数です。通常、成功メッセージの表示やフォームのリセット、ナビゲーションの実行などに使用します。
const { mutate: createPost, onDone } = useMutation(....);
// 成功時の処理
onDone(({ data }) => {
console.log('Message sent successfully:', data);
alert('メッセージが送信されました!');
});
onError
ミューテーションの実行中にエラーが発生した場合に呼び出されるコールバック関数です。エラーメッセージの表示やログの記録、ユーザーへの通知などに利用します。
const { mutate: createPost, onError } = useMutation(....);
// エラー時の処理
onError((err) => {
console.error('Error sending message:', err);
alert('メッセージの送信に失敗しました。');
});
まとめ
GraphQLをVue.jsとApollo Clientで使うことで、フロントエンド開発の柔軟性や効率がグッと上がります。
今回はデータの取得と更新の基本を解説しましたが、これらはあくまで基礎的な部分です。
プロジェクトによっては、キャッシュの扱いを細かく調整したり、型定義でコードの安全性を上げたり、Apolloのエコシステムをフル活用したりと、もっと踏み込んだ工夫が必要になってきます。そのあたりも今後しっかり学んでいきたいなと思っています!
この記事が少しでも参考になれば嬉しいです!
最後まで読んでいただき、ありがとうございました🙌
Discussion