Apollo CLI の codegen から GraphQL Code Generator に移行したくなり、移行しました。移行するにあたって、一度に全部変更すると破滅するので、以下の作戦で行いました。
- GraphQL Code Generator を導入
apollo client:codegen
と GraphQL Code Generator を共存させる- 徐々に GraphQL Code Generator で生成されたコードに置き換えていく
- 最後に
apollo client:codegen
記述を package.json から削除
といった具合で進めます。
GraphQL Code Generator を導入
こちらに導入方法をまとめてあるので良かったらどうぞ。導入だけであれば、特に既存の何かと衝突したりはせずすんなり入ると思います。 funnelbit.hatenablog.com
apollo client:codegen
と GraphQL Code Generator を共存させる
多くの場合、package.json
などにコード生成タスクを書いていて、そこで apollo client:codegen
を動かしているプロダクトが多いと思います。これはそのままにしておいてください。また GraphQL Code Generator を導入したことで GraphQL Code Generator もコード生成処理が package.json
に書かれていると思いますが、これもそのままにしてください。そして少し無茶な感じですが apollo client:codegen
と GraphQL Code Generator のコード生成を同時に走らせても破滅しないようにします。
とはいえ、デフォルトの状態では駄目な可能性があります。GraphQL Code Generator でコード生成をした後に apollo-codegen のコード生成プロセスを実行してみてください。場合によってはエラーが出ます(もしでなければここは読み飛ばしてください)。
intro-graphql-codegen ● ⍟4 npm run dev:apollo # apollo client:codegen です > app@0.1.0 dev:apollo /app > apollo client:codegen --target=typescript --outputFlat --customScalarsPrefix=GraphQL --watch ./src/types/graphql.ts CLIError: Error in "Loading queries for Unnamed Project": Error: ️️There are multiple definitions for the `GetUser` operation. Please rename or remove all operations with the duplicated name before continuing. at Object.error (/app/node_modules/@oclif/errors/lib/index.js:26:15) at Generate.error (/app/node_modules/@oclif/command/lib/command.js:60:23) at OclifLoadingHandler.showError (/app/node_modules/apollo/lib/OclifLoadingHandler.js:28:22) at OclifLoadingHandler.handle (/app/node_modules/apollo/lib/OclifLoadingHandler.js:13:18) { oclif: { exit: 2 }, code: undefined } ✖ Loading Apollo Project → Error initializing Apollo GraphQL project "Unnamed Project": Error: Error in "Loading queries for Unnamed Project": Error: ️️There are multiple definitions for the `GetUser` …
GetUser
が複数定義していると言われて怒られています。これは、GetUser
の定義が graphql-codegen によって生成されており、そこと競合しているとみなされエラーとなっているためです。
// とあるコンポーネント内。gql が定義されている ... gql` query GetUser($id: ID!) { node(id: $id) { id ... on Entry { title } } } `; ...
// GraphQL Code Generator によって作られた graphql.ts ファイル内 ... export const GetUserDocument = gql` // ここで定義されているので、二重にあると認識されている query GetUser($id: ID!) { node(id: $id) { id ... on Entry { title } } } `; ...
graphql-codegen で作られた GetUserDocument
は、各コンポーネントで import して利用すると便利なので、このままにしておきたいです。とはいえこのままでは apollo-codegen でコード生成できないので、何かしらの対策が必要になります。
そのための策としては、単純に apollo-codegen が graphql-codegen によって作られたファイルを見ないようにすると良いです。
// package.json ... "dev:apollo": "apollo client:codegen --target=typescript --includes='src/**/!(graphql).{ts,tsx}' --outputFlat --customScalarsPrefix=GraphQL --watch ./src/types/graphql.ts", ...
ポイントは --includes='src/**/!(graphql).{ts,tsx}'
で、ここで graphql-codegen によって作られたコード src/generated/graphql.ts
を含まないようにしています。この状態で apollo-codegen のコード生成を走らせるとうまくいきます。
徐々に GraphQL Code Generator で生成されたコードに置き換えていく
import や gql の宣言箇所、型などいくつか書き換える必要があります。以下に例を diff で示します。
import { gql } from "@apollo/client"; - import { CreatedWorksContent_createdWorks } from "types/graphql"; // 1 import { - createdWorkFragment, - ListCreatedWork, - } from "./elements/ListItem"; // 2 + CreatedWorkContentFragment, + CreatedWorkContentFragmentDoc, + } from "generated/graphql"; // 3 + import { ListCreatedWork } from "./elements/ListItem"; - export const createdWorksFragment = gql` // 4 + gql` fragment CreatedWorksContent on RegisteredViewer { createdWorks { id ...CreatedWorkContent } } - ${createdWorkFragment} + ${CreatedWorkContentFragmentDoc} // 5 `; interface ListCreatedProps { - createdWorks: CreatedWorksContent_createdWorks[] | null; + createdWorks?: CreatedWorkContentFragment[] | null; // 6 } export const ListCreated: React.FC<ListCreatedProps> = ({ ...
types/graphql
は apollo codegen によって生成された定義が入っている。これは利用しないので import を削除する- ここではわかりにくいですが、
ListItem
コンポーネントの中でも apollo codegen で生成されたコードを GraphQLCodegen によって生成されたコードにおきかえています。ListItem
は Fragment Collocation として props を要求しているので、結果として import が変わっている、ということになります。 generated/graphql
は graphql-codegen によって生成された定義が入っています。これからはこちらを利用するので import します。ここには 2 で削除した項目が(変数名は若干違うが中身は同じ)入っていることになります。- graphql-codegen はここに相当する変数を export してくれているので、必要がなくなります。
- 4 によって export された変数を代わりにここで使います。
- apollo codegen は Fragment 内の各キーごとに型としていました。graphql-codegen は Fragment ごとの型が生成されます。これに則ると、このコンポーネントは自身で定義した Fragment の中の型を要求するのではなく、自信で定義した Fragment そのものの型を要求する形になります。
意外と様々な箇所を変えることになるので、最も下層のコンポーネントから手を入れて、そこから影響のあるコンポーネントも変えていく、というスタイルが良さそうです。