🚀

GraphQL Code Generatorで生成したApollo Clientを使ってStar Wars APIを呼び出してみた

2023/07/26に公開

はじめに

  • GraphQL Code Generator x React を試してみた
  • 関連する記事やサンプルコードはそれなりにあって、いくつか試したけれど動かなかった
  • 試行錯誤して動くものを作れたので共有

コード

環境構築

Vite

$ npm create vite@latest hello-react-graphql-codegen                                      14:50:33
✔ Select a framework: › React
✔ Select a variant: › TypeScript

Scaffolding project in /Users/hoge/Repos/hello-react-graphql-codegen...

Done. Now run:

  cd hello-react-graphql-codegen
  npm install
  npm run dev
  • 実行してみる
cd hello-react-graphql-codegen
npm install
npm run dev
  • 動いた

GraphQL

npm i -S graphql
npm i -D typescript ts-node @graphql-codegen/cli @graphql-codegen/client-preset
  • ただ、typescriptはすでに入ってるからいらなそう。
  • スターウォーズの情報を取得できる GraphQL API(SWAPI GraphQL API)から映画情報を取得するコードになっている
  • どのクライアントでもよかったけれど、Apollo Client を試す
  • codegen.tsには、スキーマの場所や生成元・生成先のフォルダの場所を記載している
codegen.ts
import { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: 'https://swapi-graphql.netlify.app/.netlify/functions/index',
  documents: ['src/**/*.tsx'],
  ignoreNoDocuments: true, // for better experience with the watcher
  generates: {
    './src/gql/': {
      preset: 'client'
    }
  }
}

export default config
  • App.tsxで、useQueryを使ってデータ取得
src/App.tsx
import React from 'react'
import { useQuery } from '@apollo/client'

import './App.css'
import Film from './Film'
import { graphql } from '../src/gql'

const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ `
  query allFilmsWithVariablesQuery($first: Int!) {
    allFilms(first: $first) {
      edges {
        node {
          ...FilmItem
        }
      }
    }
  }
`)

function App() {
  // `data` is typed!
  const { data } = useQuery(allFilmsWithVariablesQueryDocument, { variables: { first: 10 } })
  return (
    <div className="App">
      {data && <ul>{data.allFilms?.edges?.map((e, i) => e?.node && <Film film={e?.node} key={`film-${i}`} />)}</ul>}
    </div>
  )
}

export default App
  • Film.tsxで、取得した情報のフィルムの fragment を切り出して、表示
src/Film.tsx
import { FragmentType, useFragment } from './gql/fragment-masking'
import { graphql } from '../src/gql'

export const FilmFragment = graphql(/* GraphQL */ `
  fragment FilmItem on Film {
    id
    title
    releaseDate
    producers
  }
`)

const Film = (props: {
  /* `film` property has the correct type 🎉 */
  film: FragmentType<typeof FilmFragment>
}) => {
  const film = useFragment(FilmFragment, props.film)
  return (
    <div>
      <h3>{film.title}</h3>
      <p>{film.releaseDate}</p>
    </div>
  )
}

export default Film
  • ガイドに書かれていた上記作業だけじゃ動かなかった

Update

main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import {
  ApolloClient,
  ApolloProvider,
  HttpLink,
  InMemoryCache,
} from "@apollo/client";

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: "https://swapi-graphql.netlify.app/.netlify/functions/index",
  }),
});

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>
);
  • @graphql-codegen/client-presetを入れたら、@apollo/client必要ないと思ったけれど、エラーが出たので入れた。
npm i @apollo/client
package.json
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "codegen": "graphql-codegen-esm --config codegen.ts"
  },
  • コード生成を実行
  • 動いた
npm run codegen

.graphql

  • 上記は tsx ファイルにクエリを書いていた

  • graphqlファイルにクエリを書く方も試してみる

  • まず、graphqlファイルを準備

query.graphql
query allFilmsWithVariablesQuery($first: Int!) {
  allFilms(first: $first) {
    edges {
      node {
        ...FilmItem
      }
    }
  }
}

fragment FilmItem on Film {
  id
  title
  releaseDate
  producers
}
  • codegen.tsdocumentsを修正
codegen.ts
import { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
    schema: 'https://swapi-graphql.netlify.app/.netlify/functions/index',
    documents: ['src/**/*.graphql'],
    ignoreNoDocuments: true, // for better experience with the watcher
    generates: {
        './src/gql/': {
            preset: 'client'
        }
    }
}

export default config
  • codegen する
npm run codegen
  • 生成したファイル(./gql/graphql)から、最後がDocumentの変数をインポートしてuseQueryに渡す
App.tsx
import { useQuery } from "@apollo/client";

import "./App.css";
import Film from "./Film";
import { AllFilmsWithVariablesQueryDocument } from "./gql/graphql";

function App() {
  // `data` is typed!
  const { data } = useQuery(AllFilmsWithVariablesQueryDocument, {
    variables: { first: 10 },
  });
  return (
    <div className="App">
      {data && (
        <ul>
          {data.allFilms?.edges?.map(
            (e, i) => e?.node && <Film film={e?.node} key={`film-${i}`} />
          )}
        </ul>
      )}
    </div>
  );
}

export default App;
  • fragment の方も同様
Film.tsx
import { FragmentType, useFragment } from "./gql/fragment-masking";
import { FilmItemFragmentDoc } from "./gql/graphql";

const Film = (props: {
  /* `film` property has the correct type 🎉 */
  film: FragmentType<typeof FilmItemFragmentDoc>;
}) => {
  const film = useFragment(FilmItemFragmentDoc, props.film);
  return (
    <div>
      <h3>{film.title}</h3>
      <p>{film.releaseDate}</p>
    </div>
  );
};

export default Film;
  • これで動くはず

動作確認

  • npm run devで動作確認

  • 動いた

おわりに

  • まだ、アップデート頻度が高いところなのかなと思う
  • create-react-appが非推奨っぽくなっていたり、apollographql/apollo-toolingが Deprecated になってたり、ちゃんと調べないといけない感じする
  • とりあえず、動いてよかった
GitHubで編集を提案

Discussion