GraphQL Code Generator は GraphQL のスキーマ情報やオペレーションコードをもとにし、アプリケーションが触るコード生成を行うことができるツールです。プラグイン形式で機能追加できる特徴があり、プロダクトの環境によって様々なコード生成を行うことができます。
今回は React 環境に GraphQL Code Generator を導入する手順を紹介します。
モチベーション
GraphQL のスキーマやオペレーションコードから、フロントエンド用にコード生成するツールとして最もポピュラーなのは apollo-tooling でしょうか。というのも Apollo 自体がポピュラーであり、そのドキュメントに記載があるためにこれを利用するケースが多いと思われます。apollo-tooling
でも開発に問題は有りませんが、 GraphQL Code Generator
を使うとより有益になる可能性があります。
先にも述べたとおり GraphQL Code Generator
はプラグイン形式で機能を追加できます。React かつ Next.js を利用している場合、React 用のプラグイン、Next.js 用のプラグインが利用できます。Vue.js 用もありますし、Angular 用もあります。他にも、プロダクトで利用されうる技術ごとに様々なプラグインも用意されています。これが意味するのは、GraphQL Code Generator
はアプリケーションの構成ごとに最適な機能を提供しているために、よりプロダクトの実態に合ったコード生成を実現できるということが言えます。
プロダクト構成
シンプルに考え、以下のような構成であるとします(Next.js の構成となっていますが、Next.js 特有の GraphQL Code Generator
plugin は今回利用しません)。
- src/ - pages/ - _app.tsx - index.tsx // 今回関係ある - graphql/schema.graphql // 今回関係ある - package.json - tsconfig.json - node_modules - .next
// 今回関係ある
と書かれたファイルだけが今回取り扱うファイルたちです。
index.tsx
はこのアプリケーション唯一の画面でありロジックです。ユーザが投稿した記事を表示します。関連する GraphQL 部分のコードだけ以下に示します。
// index.tsx export const ENTRY_QUERY = gql` query GetEntryQuery($id: String!) { entry } `; ... const { data } = useQuery<EntryQuery, EntryQueryVariables>( ENTRY_QUERY, { variables: { id: id, }, skip: !id, } );
schema.graphql
は GraphQL のスキーマです。これは API 側の実装によって作成されたものです。スキーマは API から配信しても良いのですが、今回はファイルをそのまま利用することにし、ここに置いています。
今回のような構成では、例えば以下のようなコード生成が可能です。
query や mutation などのオペレーション定義から React の Hooks を作成できる(コード量を削減できる)
GraphQL Code Generator 導入前、React 内では以下のように GraphQL のコードを書いていました。
// index.tsx export const ENTRY_QUERY = gql` // ENTRY_QUERY に注目 query GetEntryQuery($id: String!) { .... } `; ... const { data } = useQuery<EntryQuery, EntryQueryVariables>( // useQuery に注目 ENTRY_QUERY, // ENTRY_QUERY に注目 { variables: { id: id, }, skip: !id, } );
export const ENTRY_QUERY
は useQuery
や tsx の外、例えばテストコードで使うために宣言していたものです。quey を叩くには useQuery
を使い、ジェネリクスに型を指定します。
これでも問題はありませんが、GraphQL Code Generator を使うとよりコード量が削減できます。useQuery
を直接使わずに通信ができるようオペレーションごとに専用の Hooks が用意されるようになり、また export const ENTRY_QUERY
相当のコードが graphql-codegen
によって生成されるようになります。
... // useQuery 相当 export function useGetEntryQuery(baseOptions?: Apollo.QueryHookOptions<GetEntryQuery, GetEntryQueryVariables>) {} // useLazyQuery 相当 export function useGetEntryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetEntryQuery, GetEntryQueryVariables>) {} // ENTRY_QUERY 相当 export const GetEntryDocument = gql` query GetEntry($id: String!) { entry(id: $id) { id ...
よって ENTRY_QUERY
というような変数に入れなくとも、以下のようにできます。
import { GetEntryDocument, useGetEntryQuery } from "generated/graphql"; gql` // ENTRY_QUERY に代入しなくなった query EntryQuery($id: String!) { .... } `; ... const { data } = useGetEntryQuery({ // かつての useQuery に変わって生成された Hooks を使う variables: { id: id, }, skip: !id, }); ...
Enum を封印できる
TypeScript | GraphQL Code Generator にある enumsAsTypes
を true
にすれば、生成されるコードから enum を排除し、代わりに Union Types とすることができます。
// enumsAsTypes 設定無し export enum Animal { Dog = 'DOG', Cat = 'CAT }
// enumsAsTypes true export type Animal = | 'DOG' | 'CAT';
TypeScript の Enum は何かと話題が多いもので、気をつけて利用するよりもいっその事利用しないプロダクトも多いと思うので、こういうオプションがあるのはありがたいです。
Install
npm install --save-dev @graphql-codegen/cli
npx graphql-codegen init
するとセットアップウィザードが始まるので、答えていきます。
What type of application are you building?
$ npx graphql-codegen init Welcome to GraphQL Code Generator! Answer few questions and we will setup everything for you. ? What type of application are you building? (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◯ Backend - API or server ◯ Application built with Angular ◉ Application built with React ◯ Application built with Stencil ◯ Application built with other framework or vanilla JS
導入するプロダクトの利用フレームワークを聞かれています。今回は React で行っているので、Application build with React
を選択します。
Where is your schema?
? Where is your schema?: (path or url) (http://localhost:4000)
GraphQL スキーマの場所を聞かれています。スキーマが URL を叩くことで得れれるなら URL、直接ファイルを触るなら .graphql
の場所を指定します。今回は graphql/schema.graphql
にあるので graphql/schema.graphql
と指定しました。graphql/
ディレクトリ以下に複数の graphql
ファイルがある場合は graphql/*.graphql
とします。
Where are your operations and fragments?
? Where are your operations and fragments?: (src/**/*.graphql)
ここで要求されているのは、クライアントサイドで書かれている query や mutation といったオペレーションの定義です。今回の場合、定義は src ディレクトリ以下にある tsx にかかれています。また今後 tsx だけではなく ts ファイルにもオペレーションを書く可能性を考えて、ここに書かれているような *.graphql
ではなく "src/**/!(*.d).{ts,tsx}"
と指定します。
Pick plugins
? Pick plugins: (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ TypeScript (required by other typescript plugins) ◉ TypeScript Operations (operations and fragments) ◉ TypeScript React Apollo (typed components and HOCs) ◯ TypeScript GraphQL files modules (declarations for .graphql files) ◯ TypeScript GraphQL document nodes (embedded GraphQL document) ◯ Introspection Fragment Matcher (for Apollo Client)
プラグインを選択します。ドキュメントにそれぞれ書いてあるので見てみましょう。
- Typescript
- https://graphql-code-generator.com/docs/plugins/typescript
- Typescript でスキーマ定義からコードをはきだすために必要です(そもそも必須なので外せない)。またこれ以降に指定するプラグイン
TypeScript Operations
やTypeScript React Apollo
でも必須です。
- TypeScript Operations
- https://graphql-code-generator.com/docs/plugins/typescript-operations
- フロントエンド(Typescript)に書かれている
tsx
,ts
とかにあるオペレーション定義からコード生成するのに必要です。また TypeScript React Apollo を使うのにも必要です。
- TypeScript React Apollo
- https://graphql-code-generator.com/docs/plugins/typescript-react-apollo
- これを導入することで、各オペレーションを実行できる Rect の Hooks を生成できます。具体的には、
useQuery<GetUserQuery, GetUserVariables>()
として呼んでいるようなコードをuseGetUserQuery()
という Hooks に置き換えることができます。非常に便利なので導入します
? Where to write the output
? Where to write the output: (src/generated/graphql.tsx)
GraphQL Code Generator が生成したコードを置く場所を指定できます。tsx
である必要はないので src/generated/graphql.ts
とします。
Do you want to generate an introspection file?
? Do you want to generate an introspection file? (Y/n)
introspection file が必要なら Y を指定しましょう。ここでは必要ないので n とします。
How to name the config file?
How to name the config file? (codegen.yml)
GraphQL Code Generator は yml で設定ファイルを記述します(今までウィザードで聞かれていた設定はここに書かれます)。こだわりが無ければこのままで良いと思うのでこのまま codegen.yml
で進めます。
What script in package.json should run the codegen?
What script in package.json should run the codegen?
GraphQL Code Generator がコード生成するスクリプトを package.json に用意してくれますが、そのスクリプト名を設定できます。今回は codegen
としました。
Config file generated at codegen.yml $ npm install To install the plugins. $ npm run codegen To run GraphQL Code Generator.
完了するとこのようなメッセージが出てきます。npm i
して npm run codegen
すると src/generated/graphql.ts
が吐き出されるはずです。
ちなみに package.json には以下のようなものが追加されており
... "codegen": "graphql-codegen --config codegen.yml" "devDependencies": { ... "@graphql-codegen/typescript": "1.17.9", "@graphql-codegen/typescript-operations": "1.17.8", "@graphql-codegen/typescript-react-apollo": "2.0.6", ...
codegen.yml は新規に作成されています。
overwrite: true schema: "graphql/schema.graphql" documents: "src/**/!(*.d).{ts,tsx}" generates: src/generated/graphql.tsx: plugins: - "typescript" - "typescript-operations" - "typescript-react-apollo"
GraphQL Code Generator を理解していれば、ウィザードを使わずにこれらを直接編集しても良さそうです。
ESLint の設定
ESLint を設定されているプロダクトがほとんどだと思います。このままでは src/generated/graphql.ts
のコードに対し ESLint から大量の怒られが発生するので、除外する設定を書いておくと良さそうです。
gnorePatterns: ["src/generated/graphql.ts"],
Enum 禁止
codegen.yml に enumsAsTypes: true
を追加することで、Enum ではなく Union Types にできます。必要であれば指定しておくと良いです。
overwrite: true schema: "graphql/schema.graphql" documents: "src/**/!(*.d).{ts,tsx}" generates: src/generated/graphql.tsx: plugins: - "typescript" - "typescript-operations" - "typescript-react-apollo" config: enumsAsTypes: true // これ