Conao3 Notes

Apollo Clientことはじめ

はじめに

GraphQLクライアントのApollo Clientを使ってみたので、そのメモです。

環境構築

以下の記事を参考に進めます。

Create Next app

$ pnpm create next-app react-apollo-client
.../share/pnpm/store/v3/tmp/dlx-3829487  |   +1 +
Packages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: /home/conao/.local/share/pnpm/store/v3
  Virtual store is at:             ../../../.local/share/pnpm/store/v3/tmp/dlx-3829487/node_modules/.pnpm
.../share/pnpm/store/v3/tmp/dlx-3829487  | Progress: resolved 1, reused 1, downloaded 0, added 1, done
✔ Would you like to use TypeScript with this project? … No / Yes: Yes
✔ Would you like to use ESLint with this project? … No / Yes: Yes
✔ Would you like to use Tailwind CSS with this project? … No / Yes: Yes
✔ Would you like to use `src/` directory with this project? … No / Yes: Yes
✔ Use App Router (recommended)? … No / Yes: Yes
✔ Would you like to customize the default import alias? … No / Yes: Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /home/conao/dev/tmp/git/react-apollo-client.

pnpm run dev でローカルサーバーが起動します。

$ cd react-apollo-client
$ pnpm run dev

不要ファイルの削除

publicの画像や不要な設定を削除します。

5 files changed, 5 insertions(+), 141 deletions(-)
public/next.svg     |   1 -
public/vercel.svg   |   1 -
src/app/globals.css |  24 -----------
src/app/page.tsx    | 112 ++--------------------------------------------------
tailwind.config.js  |   8 +---

Apollo Clientのインストール

Apollo Clientをインストールします。

$ pnpm add @apollo/client graphql

単純な読み込みで動作確認します。

import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://spacex-production.up.railway.app/',
  cache: new InMemoryCache(),
});

client
  .query({
    query: gql`
    {
      launchesPast(limit: 10) {
        mission_name
        launch_date_local
        launch_site {
          site_name_long
        }
        links {
          article_link
          video_link
        }
        rocket {
          rocket_name
        }
      }
    }
    `,
  })
  .then((result) => console.log(result));

page.tsx に書いた場合はサーバーで実行されるので、ターミナルに結果が表示されます。

- event compiled client and server successfully in 280 ms (685 modules)
{
  data: {
    launchesPast: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object]
    ]
  },
  loading: false,
  networkStatus: 7
}

useQueryの利用

Reactのコンポーネントで利用するために、 useQuery を利用します。 適当なところで ApolloProvider でラップすることで、その下のコンポーネントで利用できます。

ブラウザのNetworkでリクエストが見たいのでクライアント側からリクエストを送るようにします。 page.tsxの先頭に 'use client' を追加します。

'use client';

import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client';
import { LaunchesList } from './components/LaunchesList';

const client = new ApolloClient({
  uri: 'https://spacex-production.up.railway.app/',
  cache: new InMemoryCache(),
});

export default function Page() {
  return (
    <ApolloProvider client={client}>
      <main>
        <div>
          <p className="font-bold underline">Hello, world!</p>
        </div>
        <LaunchesList />
      </main>
    </ApolloProvider>
  )
}
'use client';

import { useQuery, gql } from '@apollo/client';

const LAUNCHES_LIST = gql`
  {
    launchesPast(limit: 10) {
      id
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      links {
        article_link
        video_link
      }
      rocket {
        rocket_name
      }
    }
  }
  `;

export function LaunchesList() {
  const { loading, error, data } = useQuery(LAUNCHES_LIST);

  if (loading) return <div>loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <ul>
      {data.launchesPast.map((launch: any) => (
        <li key={launch.id}>{launch.mission_name}</li>
      ))}
    </ul>
  )
}

TypeScript統合

TypeScript with Apollo Client を参考にTypeScriptと統合します。

$ pnpm add -D typescript @graphql-codegen/cli @graphql-codegen/client-preset

また、私が生成したときは @graphql-typed-document-node/core に依存したコードが生成されたので、それもインストールしておきます。

$ pnpm add -D @graphql-typed-document-node/core

codegen.tsを以下の内容で作成します。URLのところは対象のEndpointに変更が必要です。

import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: '<URL_OF_YOUR_GRAPHQL_API>',
  documents: ['src/**/*.tsx'],
  generates: {
    './src/__generated__/': {
      preset: 'client',
      plugins: [],
      presetConfig: {
        gqlTagName: 'gql',
      }
    }
  },
  ignoreNoDocuments: true,
};

export default config;

package.jsonにジョブを追加します。

"scripts": {
  "gen": "graphql-codegen"
},

実行します。

$ pnpm run gen

> react-apollo-client@0.1.0 gen /home/conao/dev/tmp/git/react-apollo-client
> graphql-codegen

✔ Parse Configuration
✔ Generate outputs

そうすると __generated__ ファルダが生成されます。

__generated__ の gql を使うことで型が付きます。便利。

'use client';

import { useQuery } from '@apollo/client';
import { gql } from '../../__generated__/gql';

const LAUNCHES_LIST = gql(/* GraphQL */`
  query LaunchList {
    launchesPast(limit: 10) {
      id
      mission_name
      launch_date_local
      launch_site {
        site_name_long
      }
      links {
        article_link
        video_link
      }
      rocket {
        rocket_name
      }
    }
  }
`);

export function LaunchesList() {
  const { loading, error, data } = useQuery(LAUNCHES_LIST);

  if (loading) return <div>loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <ul>
      {data?.launchesPast?.map((launch) => (
        <li key={launch?.id}>{launch?.mission_name}</li>
      ))}
    </ul>
  )
}