Closed23

GraphQL Code Generator 導入する

"自動生成" って何が生成されるの...?

今までこう↓書かなきゃいけなかったものが、

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


interface PostQuery {
  posts: {
    id: string
    title: string
    author?: {
      id: string
      firstName: string
      lastName: string
    }
  }[]
}

const postsQueryDocument = gql`
  query Posts {
    posts {
        id
        title
        author {
          id
          firstName
          lastName
        }
    }
  }
`

const Posts = () => {
  const { data } = useQuery<PostQuery>(postsQueryDocument);

  // ...
}

これ↓で良くなる!

import { useQuery } from '@apollo/client';
import { postsQueryDocument } from './graphql/generated';


const Posts = () => {
  const { data } = useQuery(postsQueryDocument);

  // `result` is fully typed!
  // ...
}

hooks の自動生成も可能

別途 plugin を利用し、しかるべき設定をすれば、Apollo Client の hooks を自動生成することもできるとのこと!こんな感じ↓

import { usePostsQuery } from './graphql/generated';


const Posts = () => {
  const { data } = usePostsQuery();

  // `result` is fully typed!
  // ...
}

下準備

Install

https://www.graphql-code-generator.com/docs/getting-started/installation

まずは公式Docsの Installation に従って必要な package を Install する。

npm install graphql
npm install @graphql-codegen/cli

今回導入対象の既存 project にはすでに graphql は入っているので、@graphql-codegen/cli のみ instatll

version はこちら↓

"graphql": "^15.8.0",
"@graphql-codegen/cli": "^2.4.0",

Setup

https://www.graphql-code-generator.com/docs/getting-started/installation#setup

Initialization Wizard#
Once installed, GraphQL Code Generator CLI can help you configure your project based on some popular flows:

Wizard に従ってセットアップを進めてみる。

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? Application built with React
? Where is your schema?: (path or url) /api/graphql
? Where are your operations and fragments?: graphql/schema.graphql
? Pick plugins: TypeScript (required by other typescript plugins), TypeScript O
perations (operations and fragments), TypeScript React Apollo (typed components
 and HOCs)
? Where to write the output: graphql/generated/graphql.tsx
? Do you want to generate an introspection file? Yes
? How to name the config file? codegen.yml
? What script in package.json should run the codegen? codegen
Fetching latest versions of selected plugins...

    Config file generated at codegen.yml
    
      $ npm install

    To install the plugins.

      $ npm run codegen

    To run GraphQL Code Generator.

What type of application are you building

今回の project は Next.js ベースなので、"Application built with React" のみ選択。

Where are your operations and fragments

Nexus で schema.graphql を自動生成しているので、その path を指定。

Pick plugins: (Press <space> to select, <a> to toggle all, <i> to invert sele
ction, and <enter> to proceed)
❯◉ 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)
◯ Urql Introspection (for Urql Client)

とりあえず、デフォルトで選択されていた以下 3つを選択

  • TypeScript (required by other typescript plugins)
  • TypeScript Operations (operations and fragments)
  • TypeScript React Apollo (typed components and HOCs)

Where to write the output

graphql 関連は graphql/ 下にまとめてるので、graphql/generated/graphql.tsx

Where is your schema?:

graphql の endpoint の path を指定 /api/graphql

Do you want to generate an introspection file?

何を introspect するのかわからないけど、とりあえず yes にしてみる

How to name the config file?

デフォの codegen.yml

What script in package.json should run the codegen?

とりあえず codegen で

これで完了。
以下 codegen.yml ファイルが生成され、package.json に codegen script が追加された。

codegen.yml
overwrite: true
schema: "/api/graphql"
documents: "graphql/schema.graphql"
generates:
  graphql/generated/graphql.tsx:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"
  ./graphql.schema.json:
    plugins:
      - "introspection"
package.json
 "scripts": {
    "codegen": "graphql-codegen --config codegen.yml"
  },

実行してみる

schema でエラー

実際にコマンド叩いてみたところエラーが出たので対応していく

npm run codegen
 ✔ Parse configuration
  ❯ Generate outputs
    ❯ Generate graphql/generated/graphql.tsx
      ✖ Load GraphQL schemas
        → Failed to load schema
        Load GraphQL documents
        Generate
    ❯ Generate ./graphql.schema.json
      ✖ Load GraphQL schemas
        → Failed to load schema
        Load GraphQL documents
        Generate

どうやら、schema file がロードできてないらしい
現状、こう↓なってるが path の指定だけだとダメみたい

schema: "/api/graphql"

公式 Docs を見ていく

https://www.graphql-code-generator.com/docs/config-reference/schema-field

localhost の url を指定すればよさそう

schema: http://localhost:3000/api/graphql

これで行けた

documents でエラー

次は Load GraphQL documents でエラー起きてるので対応していく。

 ✔ Parse configuration
  ❯ Generate outputs
    ❯ Generate graphql/generated/graphql.tsx
  ✔ Parse configuration
  ❯ Generate outputs
    ❯ Generate graphql/generated/graphql.tsx
      ✔ Load GraphQL schemas
      ✖ Load GraphQL documents
        →           - graphql/schema.graphql
        Generate
    ❯ Generate ./graphql.schema.json      ✔ Load GraphQL schemas      ✖ Load GraphQL documents
        →           - graphql/schema.graphql
        Generate

現状は、こう↓

documents: "graphql/schema.graphql"

そもそも、"GraphQL Documents" とは何なのか理解してないな...そっち優先だな

GraphQL Document とは...?

まずは GraphQL Code Generator の documents に関する説明を見てみる

https://www.graphql-code-generator.com/docs/config-reference/documents-field

The documents field should point to your GraphQL documents: query, mutation, subscription and fragment

query, mutation, subscription and fragment ... もっと説明が欲しいな

他の説明をググる。

https://www.apollographql.com/blog/graphql/basics/the-anatomy-of-a-graphql-query/

GraphQL document: A string written in the GraphQL language that defines one or more operations and fragments.

これだ。
"GraphQL Document" とは、GraphQL へ投げる query やら mutation やらの string のことを指すのか。

つまり、下の例の場合、

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

const GET_GREETING = gql`
  query GetGreeting($language: String!) {
    greeting(language: $language) {
      message
    }
  }
`;

function Hello() {
  const { loading, error, data } = useQuery(GET_GREETING, {
    variables: { language: 'english' },
  });
  if (loading) return <p>Loading ...</p>;
  return <h1>Hello {data.greeting.message}!</h1>;
}

gql に渡しているこの↓部分が GraphQL Document と。

`
  query GetGreeting($language: String!) {
    greeting(language: $language) {
      message
    }
  }
`

Apollo Client の API reference で useQuery の型見にいく。

function useQuery<TData = any, TVariables = OperationVariables>(
  query: DocumentNode,
  options?: QueryHookOptions<TData, TVariables>,
): QueryResult<TData, TVariables> {}

DocumentNode という型が "GraphQL Document" に対応してるのね

エラー対応に戻る

documents のエラー対応

今回の project の場合、GraphQL Documents は現状存在しないので消すことで対応。

最後のエラー

次は Generate でエラーでたので対応していく。

✔ Parse configuration
  ❯ Generate outputs
    ❯ Generate graphql/generated/graphql.tsx
      ✔ Load GraphQL schemas
      ✔ Load GraphQL documents
      ✖ Generate
        → Unable to find template plugin matching typescript-operations
    ❯ Generate ./graphql.schema.json
      ✔ Load GraphQL schemas
      ✔ Load GraphQL documents
      ✖ Generate
        → Unable to find template plugin matching introspection

plugins は package.json に追記されただけで、 install されてなかったので install する。

npm i

もう一度実行

 ✔ Parse configuration
  ❯ Generate outputs
  ✔ Parse configuration
  ✔ Generate outputs

完了!

修正後の codegen.yml

codegen.yml
overwrite: true
schema: http://localhost:3000/api/graphql
generates:
  graphql/generated/graphql.tsx:
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-react-apollo'
  ./graphql.schema.json:
    plugins:
      - 'introspection'

この状態で npm run codegen すると、graphql/generated/graphql.tsx が生成される。
graphql/generated/graphql.tsx には、schema.graphql に対応する型定義が記載されている。

introspection は何をしているかは別途調べる

Apollo Client 用のセットアップ

ここから Apollo Client 用の自動生成ができるようにセットアップしていく。

https://www.graphql-code-generator.com/docs/guides/react#apollo-and-urql

1. Move all your GraphQL documents in dedicated .graphql files

GraphQL documents を ts, tsx から .graphql ファイルへ移動させることがファーストステップ。必須ではないがやっておくといいとのこと。

今回は既存のものは存在しないので、新規の .graphql を作成していく。
サンプルで Post を query するケースを想定して作ってみる。

posts.graphql を作成。

posts.graphql
query Posts {
  posts {
    id
    title
    content
  }
}

2. Install the @graphql-codegen/typed-document-node plugin

以下の package を install する。

npm install @graphql-codegen/typed-document-node
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations

install された version はこちら↓

package.json
    "@graphql-codegen/typed-document-node": "^2.2.3",
    "@graphql-codegen/typescript": "^2.4.3",
    "@graphql-codegen/typescript-operations": "^2.2.4",

3. Configure the plugin

  • documents に Client-side で利用する .graphql のファイルの path を指定
  • 先ほど install した 3つの plugin を指定

こんな感じ↓

codegen.yml
schema: http://my-graphql-api.com/graphql
documents: './src/**/*.graphql'
generates:
  ./src/graphql-operations.ts:
    plugins:
      - typescript
      - typescript-operations
      - typed-document-node

4. Run the codegen and update your code

あとは codegen を実行すればOK

npm run generate

src/graphql-operations.ts が自動生成され、PostsQuery のデータ型と PostsDocumentという GraphQL Document が生成されているここを確認。

src/graphql-operations.ts
export type PostsQuery = { __typename?: 'Query', posts?: Array<{ __typename?: 'Post', id: string, title: string, content: string } | null> | null };

export const PostsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Posts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"posts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"content"}}]}}]}}]} as unknown as DocumentNode<PostsQuery, PostsQueryVariables>;

実際に使ってみる

自動生成された型と GraphQL Document を useQuery() に渡してあげる。

Posts.tsx
const Posts = () => {
  const { data } = useQuery<PostsQuery>(PostsDocument);

  // ...
}

型補完される!最高!

Apollo Client の hooks を自動生成

GraphQL Document の自動生成までできたので、次は hooks の自動生成ができるようにセットアップしていく。

https://www.graphql-code-generator.com/docs/guides/react#typed-hooks-for-apollo-and-urql

1. Install the @graphql-codegen/typescript-react-apollo

以下3つの plugin が必要になるので install する。

npm install @graphql-codegen/typescript-react-apollo
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations

GraphQL Document の自動生成のセットアップの段階で @graphql-codegen/typescript@graphql-codegen/typescript-operations はすでに install しているので、別途 install するのは @graphql-codegen/typescript-react-apollo のみ。

今回 install された version はこちら↓

package.json
    "@graphql-codegen/typescript": "^2.4.3",
    "@graphql-codegen/typescript-operations": "^2.2.4",
    "@graphql-codegen/typescript-react-apollo": "^3.2.4",

2. Configure the plugin

次は codegen.yml の設定値を更新する。
@graphql-codegen/typescript-react-apollo を追加し、config.withHooks を true に設定するだけでOK。

こんな感じ↓

code
schema: http://my-graphql-api.com/graphql
documents: './src/**/*.tsx'
generates:
  graphql/generated.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
    config:
      withHooks: true

3. Run the codegen and update your code

以上でセットアップは完了! codegen を実行する。

以下の query operation から自動生成すると、

query Posts {
  posts {
    id
    title
    content
  }
}

下のようなコードが作成される。


export type PostsQueryVariables = Exact<{ [key: string]: never; }>;


export type PostsQuery = { __typename?: 'Query', posts?: Array<{ __typename?: 'Post', id: string, title: string, content: string } | null> | null };


export const PostsDocument = gql`
    query Posts {
  posts {
    id
    title
    content
  }
}
    `;

/**
 * __usePostsQuery__
 *
 * To run a query within a React component, call `usePostsQuery` and pass it any options that fit your needs.
 * When your component renders, `usePostsQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const { data, loading, error } = usePostsQuery({
 *   variables: {
 *   },
 * });
 */
export function usePostsQuery(baseOptions?: Apollo.QueryHookOptions<PostsQuery, PostsQueryVariables>) {
        const options = {...defaultOptions, ...baseOptions}
        return Apollo.useQuery<PostsQuery, PostsQueryVariables>(PostsDocument, options);
      }
export function usePostsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PostsQuery, PostsQueryVariables>) {
          const options = {...defaultOptions, ...baseOptions}
          return Apollo.useLazyQuery<PostsQuery, PostsQueryVariables>(PostsDocument, options);
        }
export type PostsQueryHookResult = ReturnType<typeof usePostsQuery>;
export type PostsLazyQueryHookResult = ReturnType<typeof usePostsLazyQuery>;
export type PostsQueryResult = Apollo.QueryResult<PostsQuery, PostsQueryVariables>;

GraphqlDocument とそのデータ型も自動生成されていることがわかる。@graphql-codegen/typescript-react-apollo を入れれば、@graphql-codegen/typed-document-node は必要ないみたい。

実際に使ってみる

import { usePostsQuery } from './graphql/generated';

const Posts = () => {
  const { data } = usePostsQuery();

  // ...
}

型補完効いている!素晴らしい!

今回の project では Apollo Client を利用しているため、Apollo Client 向けのセットアップを行ったが、GraphQL Code Generator は React Query や URQL などの GraphQL Client のメジャーどころはカバーしているので、今後も利用していきたい。

このスクラップは2022/02/09にクローズされました
ログインするとコメントできます