🚀
GraphQL Code Generatorで生成したApollo Clientを使ってStar Wars APIを呼び出してみた
はじめに
- GraphQL Code Generator x React を試してみた
- 関連する記事やサンプルコードはそれなりにあって、いくつか試したけれど動かなかった
- 試行錯誤して動くものを作れたので共有
コード
- 今回のコードはここにおいた
-
git clone
してnpm install
してnpm run dev
したら動くはず - optimisuke/hello-react-graphql-codegen
環境構築
Vite
- 今回、vite を使って、React の環境を構築した
- create-react-app はもう古いらしいので。。。
- Create React App は役割を終えました
$ 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
- GraphQL Code Generator のドキュメントを参考に作業してみる
- (Guide: React and Vue – GraphQL Code Generator)
- 色々インストール
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
- TypeScript with Apollo Client - Apollo GraphQL Docsも参考にした
- こっちも Get started with Apollo Client - Apollo GraphQL Docs
-
main.tsx
でApolloClient()
のインスタンスを作成して、<ApolloProvider>
で囲うことで、client を使ってuseQuery
を呼べるようにする
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
-
codegen
のスクリプトも動くように変更 - この資料も参考にした → ESM TypeScript Usage – GraphQL Code Generator
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.ts
のdocuments
を修正
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;
- これで動くはず
動作確認
- SWAPI GraphQL APIから動作確認
-
npm run dev
で動作確認
- 動いた
おわりに
- まだ、アップデート頻度が高いところなのかなと思う
-
create-react-app
が非推奨っぽくなっていたり、apollographql/apollo-toolingが Deprecated になってたり、ちゃんと調べないといけない感じする - とりあえず、動いてよかった
Discussion