16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

この記事は YAMAP エンジニア Advent Calender 2021 5日目の記事です。

この記事について

フロントエンドの GraphQL 導入においての学び、備忘録を共有する目的で書きました。

この記事では、Apollo Client を用いて React アプリケーションに GraphQL を導入する具体的な方法をまとめています。

Apollo Client とは

簡単に言うと、Web フロントエンド環境(React が主で、integration により他フレームワークに対応)で GraphQL を扱うためのイケてるライブラリです。キャッシュ機構や状態管理機構も備えており、モダンで非常に理解しやすいインターフェースを有しています。

Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI.

セットアップ

Nextjs アプリケーションの構築が出来ていることを前提で進めます。

Nextjs の導入はこちら

開発環境

以下の開発環境を想定しています。

  • エディタ
    • VSCode
  • 言語
    • JavaScript または TypeScript
  • フレームワーク

ライブラリをインストール

まずは Apollo Client 関連のライブラリをインストールします。

$ yarn add @apollo/client graphql

$ yarn add -D apollo

エディタ拡張機能をインストール

VSCode に Apollo GraphQL Extension をインストールします。

これによって、後述する GraphQL スキーマに基づき、クエリのコード補完がエディタ上で機能するようになります(開発効率爆上がり間違いなしですね・・)。

Apollo の設定ファイルを作成

次に、プロジェクトルートに apollo.cofig.js ファイルを作成します。

apollo.config.js
module.exports = {
  client: {
    includes: ['./src/**/*.js'],
    service: {
      name: 'yamap-app',
      url: 'https://api.yamap.com/graphql',
    },
  },
};

client.service.url には GrapQL API のエンドポイントを記述します。

ちなみに Apollo Studio というサービスもあって、これを 使えば幸せになれるという言い伝えがあるようです。 使っている場合、service で登録している Graph の name を指定すれば OK のようです。

apollo-config.js
module.exports = {
  client: {
    includes: ['./src/**/*.js'],
    service: 'my-graph-in-apollo-studio'
  },
};

スキーマをダウンロード

あらかじめインストールしておいた apollo パッケージを使ってスキーマをダウンロードします。

$ yarn apollo service:download -c ./apollo.config.js graphql-schema.json

graphql-schema.json がプロジェクトルートに生成されたと思います。これによって、エディタ(VSCode)上でクエリのコード補完が効くようになります。

実装

実際に Apollo Client を使ってユーザー一覧データを取得してみます。

apolloClient の実装

@apollo/client パッケージに含まれる ApolloClient クラスを使います。

apollo-client.js
import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://api.yamap.com/graphql",
  cache: new InMemoryCache({}),
});

export default client;

ApolloProvider を組み込む

Nextjs アプリケーションの場合、pages 配下に _app.js ファイルを作成し、Custom App を実装する必要があります。

_app.js
import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client"; // さっき作った apolloClient を import

function CustomApp({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default CustomApp;

データの取得(CSR)

まずは useFetch() を使ったクライアントサイドでのデータ取得です。

クエリ

users-query.js
import { gql } from "@apollo/client"

export const GET_USERS_QUERY = gql`
  query ListUsers($first: Int!, $after: String) {
    users(first: $first, after: $after) {
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
        startCursor
      }
      edges {
        cursor
        node {
          id
          databaseId
          nickname
        }
      }
    }
  }
`

クライアントサイドフェッチ

pages/users.js
import { useQuery } from "@apollo/client"
import { GET_USERS_QUERY } from "../users-query"

function Users () {
  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      first: 10,
    }
  });

  const users = data?.users.edges.map(edge => edge.node);

  if (loading) {
    return <div>Loading...</div>
  }

  if (error) {
    console.error(error);
    return null;
  }

  return (
    <div>
      <h1>ユーザー一覧</h1>
      <div>
        {users?.map(user => (
          <div key={user.id}>
            {user.nickname}
          </div>
        ))}
      </div>
    </div>
  )
}

export default Users;

データの取得(SSR)

クエリは先述の GET_USERS_QUERY を流用します。

サーバーサイドフェッチ

pages/users.js
function Users ({ initialData, initialError }) {
  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      ssr: false, // getServerSideProps によって、既にサーバーサイドでデータ取得済のため
      first: 10,
    }
  });

  if (!initialData && loading) {
    return <div>Loading...</div>
  }

  if (initialError || error) {
    console.error(error);
    return null;
  }

  const users = data?.users.edges.map(edge => edge.node) || initialData?.users.edges.map(edge => edge.node);

  return (
    <div>
      <h1>ユーザー一覧</h1>
      <div>
        {users?.map(user => (
          <div key={user.id}>
            {user.nickname}
          </div>
        ))}
      </div>
    </div>
  )
}

export async function getServerSideProps() {
  const { data, error } = await client.query({
    query: QUERY,
    variables: {
      first: 10,
    }
  });

  return {
    props: {
      initialData: data,
      initialError: error,
    },
 };

以上です

ページネーションを想定しなければ SSR のコードはもう少しシンプルになるかもしれません。

参考になれば幸いです。

16
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?