👻

Vue ApolloでGraphQLを使ったデータ取得の基本ガイド

2024/12/26に公開

はじめに

現代の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ファイルに以下のコードを追加します。

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を指定します。

次に以下のコードを追加します。

main.js
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)を組み合わせてキャッシュキーを作成し、データが更新された際に、そのキーに基づいて変更された情報が自動的にキャッシュに反映されます。

次に以下のコードを追加します。

main.js
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サーバーのエンドポイントを設定したhttpLinkcache(キャッシュ機能)を渡します。

最後に以下のコードを追加します。

main.js
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クエリを実行し、その結果をリアクティブに管理するための手段です。

App.vue
<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を指定して特定のユーザー情報を取得する場合、以下のようにクエリを変更できます。

App.vue
<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はデータの作成・更新・削除といった操作を行います。

CreatePost.vue
<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タグの中に新規投稿作成に必要なミューテーションを定義します。

CreatePost.vue
gql`
mutation createPost($input: createPostInput!) {
  createPost(input: $input) {
    id
    title
    text
  }
}
`,
  • createPostはGraphQLサーバー側で定義されているミューテーションの名前です。これを呼び出すことで、サーバー側で新しい投稿が作成されます。
  • ミューテーションの引数として$inputが指定されています。$inputには、新規投稿のデータを含むオブジェクトが含まれます。これは createPostInput!型で定義されているため、この形式でデータを渡す必要があります。
  • ミューテーションが成功した場合、サーバーからレスポンスとして新規に作成された投稿のデータ(id,title,text)が返されます。

次にvariablesを使用してcreatePostの引数にinput型の値を渡すようにします。

CreatePost.vue
() => ({
    variables: {
      input: {
        userId: userId.value,
        title: newTitle.value,
        text: newMessage.value,
      }
    }
})
  • variablesは、クエリやミューテーションに対して、パラメータを動的に渡すためのオブジェクトです。
  • variablesを使用して、inputオブジェクトの中に userIdtitletextなどの必要な情報を含めて、ミューテーションの引数としてサーバーに渡しています。

次にrefetchQueriesawaitRefetchQueriesの役割について説明します。

CreatePost.vue
// クエリの際実行
refetchQueries: [{ query: GET_POST }],
// refetchQueriesが完了するまで待機
awaitRefetchQueries: true,
  • refetchQueriesは、ミューテーションが成功した後に、特定のクエリを再実行(リフェッチ)するために使います。指定されたクエリ(この場合はGET_POST)を再度実行し、サーバーから最新のデータを取得します。
  • awaitRefetchQueries: truerefetchQueriesで指定したクエリのリフェッチが完了するまで待機するためのオプションです。
  • ミューテーションが完了してすぐに次の処理に進むのではなく、リフェッチされるクエリ(ここではGET_POST)が完了するまで待機します。これにより、最新のデータを正確に取得し、その後の処理を行うことができます。
  • この2つの設定によって、ミューテーション後のデータ整合性を保ちながら、クライアント側に最新の情報が正確に反映されるようにしています。

おまけ

GraphQLミューテーションを実行する際、操作の成功や失敗に応じて適切な処理を行いたい時があると思います。その際に、Vue Apolloでは、useMutationを使用する際に、onDoneonErrorのコールバックを活用することで、これらの状況に対応することができます!(便利ですね👏)

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