Closed29

[memo]クライアント側で graphql-code-generator を使う

batibati

普段は Notion でメモをとっているが、スクラップを使ってみたかったので実験的に使ってみる。
良さそうな方を今後使ってみたい。

batibati

codegen.ts にschema, documents プロパティを記載。

  • schema にはサーバー側のスキーマを指定。
  • documents にはクライアント側のクエリやミューテションを指定。
batibati

codegen 実行時に以下エラーが発生したので、documents の末尾に / を追記

 ✖ [client-preset] target output should be a directory, ex: "src/gql/". Make sure you add "/" at the end of the directory path 
batibati

ドキュメントを参考に組み込んでみたところ、TypeScript に怒られた。
useMutation の Argument が unknown である。

生成されたファイルに飛んでみたところ、unknown を返すだけの function とそれっぽい mutation が書かれた function の2つが生成されている。

クライアント側で後者の方を指定できたら良いと思うので、その方法を探る。(多分このあたりは TypeScript の知識

batibati

function のオーバーロードが使われているみたい。
一致しないクエリ、ミューテーションの場合は unknown を返す function が呼ばれるようになっている。

batibati

ただ引数には一致するミューテーションを渡せているように思うが、解消されない。
他の要因を探す。

batibati

生成された fucntion の引数の改行文字と呼び出し時に指定した引数の改行の位置がズレていたことが原因。
ただこれらはフォーマッターで自動に改行されるものなので困った。

batibati

生成されたコードの改行位置のルールが良くわからない。
元にしたミューテーションの改行位置とも微妙に違うので、それに合わせようとするとなかなかに使いづらい。(自動でフォーマットがされる。そもそも読みづらい等々...

batibati

オプションとかに生成時のフォーマットのルールとかが指定できないかドキュメントを探ってみる

batibati

試しに元となるミューテーションをワンラインにしてみる。
これで生成される関数の引数がどうなるか見てみる。

batibati

特に改行の位置に変更はなかった。
おそらく codegen 側の独自の改行ルールがありそう。

batibati

サンプルを見た。
自分との違いは codegen.ts の documents フィールドの設定。
自分の理解では、documents に使用したいクエリがあり、それを素に codegen されると思っているが、サンプルの方で指定しているファイルでは既に codegen されたファイルが import されている。

そもそも codegen しないと import ができない(という理解)ので、順序が逆のように思える。
おそらくここが原因。

batibati

自分がやっていること

  • 使用するクエリ文字列を任意の *.grphql ファイルに記載。
  • documents にそのパスを指定。
  • codegen を実行。
  • 生成された関数を素に、useMutation を実行しようとする(ここで function が unknown を返す。

サンプルでやっていること

  • useMutation, useQuery を App.tsx に記載。クエリも同じファイルに文字列で記載。
    • 同じファイルに import 文で生成される gql ディレクトリを指定。
  • documents にそのパスを指定。
batibati

試しに documents の位置をサンプルに併せて修正してみる。
それで生成されるファイルの差分を見る。そもそも codegen がうまくいくかも怪しい気がする。

batibati

codegen は正常に実行された。
function の引数に当てられている改行位置も想定通りの位置で生成されている。

batibati

こうなってくると codegen 初回実行のときはどうなるのか。。
gql ディレクトリは生成されていないはずなので、import でコンパイルエラーが出そうなものだけど。。

batibati

gql ディレクトリをまるっと消して codegen を実行してみた。
import はもちろんエラーが出ていたが codegen は実行できた。かつ、中身は同じファイルが生成されていた。

このことから考えられるのは、以下のようなこと。

  • documents には実際に実行するクエリ文字列の場所を指定する。
  • codegen を実行すると、codegenerator その文字列をよしなに読み取って generate してくれる。
  • それを import して、クエリ文字列を型安全にし、useQuery の引数に入れる。

サンプルリポジトリはこれらが実行された最終形だから、先に import が定義されているように見える。

batibati

graphql-code-generator のドキュメントを読み返す。
https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config#configuration-options

documents - Array of paths or glob patterns for files which export GraphQL documents using a gql tag or a plain string; for example: ./src/**/*.graphql.

  • gql tag
    • これは graphql-tag のライブラリにある関数のこと。
      e.g.)
import { gql } from '@apollo/client';

const MUTATION_STR = gql`
  mutation addHoge(
    $lists: [list]
  ) {
   addHoge(
      addHoge: $lists
    ) {
        hoge
        fuga
    }
  }
`
  • a plain text
    • 文字列リテラル。シングルクォートかダブルクォートで囲んだもの。
    • $ を使いたい場合はこちらは使えず。バッククォートを使う必要がある。
batibati

自分の場合は、graphql-tag を使っていたため、codegen 対象となり生成されていたみたい。

batibati

ただやっぱり生成とコーディングの順番が逆なのがしっくりとこない。

  • 操作用のクエリ(文字列リテラル)がある
  • それを素に codegen。
  • codegen を素に apollo client でクエリ発行

という感じが自然な気がしている。

batibati

結局困っていたことは、改行の位置が合わないことによる graphql のオーバーロードの失敗。
なのでそこを上手くいくように修正できれば行けそうな気がする。

仮説

  • gql-tag を上手く使う
  • 生成された hook を上手く使う
batibati

useMutation の引数に入れる文字列をシングルクォートにしたらエラーは起きずに解消した。

従来のやり方でだめだった理由は以下みたい

  • バッククォートで定義していた。
  • そのせいでフォーマッターが聞いて改行や半角スペースがズレた。

ただ今のところ codegen を使用している旨味が感じられていないので、もう少し理解を深める。

batibati

codegen で生成されたクエリを引数にした際に生じたメリット

  • 実行関数の型定義が any, OperationVariables から具体的な型で定義されていた。
    • これによって変数として設定するインプットパラメータが型安全になる。
batibati

既存コードを codegen のものに適切に組み込むことができたので、目標は達成できた。

batibati

まとめ

GraphQL スキーマ とGraphQLクエリやミューテーション というのは異なるものを指している

  • GraphQL スキーマ:
    • データの型や関連性、クエリやミューテーションのエントリーポイントなどを定義するもの。
    • サーバーサイドに記載する。
    • codegen.ts の schema フィールドに指定する。
// e.g.)
type Post {
  id: ID!
  title: String!
  content: String!
}

type Query {
  post(id: ID!): Post
}
  • GraphQLクエリやミューテーション:
    • クライアントがサーバーに対してデータを要求するためのもの。
    • クライアントサイドで記載する。
      • codegen.ts の documents フィールドに指定する。
// e.g.)
query GetPost($postId: ID!) {
  post(id: $postId) {
    id
    title
    content
    author {
      id
      name
    }
  }
}
  • クライアント側で codegen を行うとスキーマの型以外に graphql() 関数が生成される。
    • graphql() 関数が生成される条件。FYI) https://zenn.dev/link/comments/cb36383475fa1a
    • これを用いて操作用のクエリやミューテーションを定義すると、型安全に useQuery, useMutation を使用することができるようになる。
このスクラップは2023/12/19にクローズされました