[memo]クライアント側で graphql-code-generator を使う
クライアント側で graqhql-code-generator を使うときの作業メモ
やりたいこと
- サーバー側の schema ファイルをもとに型を定義する
- 型安全なクライアントコードを記載する
FYI) https://the-guild.dev/graphql/codegen/docs/guides/react-vue
普段は Notion でメモをとっているが、スクラップを使ってみたかったので実験的に使ってみる。
良さそうな方を今後使ってみたい。
codegen.ts にschema
, documents
プロパティを記載。
- schema にはサーバー側のスキーマを指定。
- documents にはクライアント側のクエリやミューテションを指定。
codegen 実行時に以下エラーが発生したので、documents の末尾に /
を追記
✖ [client-preset] target output should be a directory, ex: "src/gql/". Make sure you add "/" at the end of the directory path
ドキュメントを参考に組み込んでみたところ、TypeScript に怒られた。
useMutation の Argument が unknown である。
生成されたファイルに飛んでみたところ、unknown を返すだけの function とそれっぽい mutation が書かれた function の2つが生成されている。
クライアント側で後者の方を指定できたら良いと思うので、その方法を探る。(多分このあたりは TypeScript の知識
function のオーバーロードが使われているみたい。
一致しないクエリ、ミューテーションの場合は unknown を返す function が呼ばれるようになっている。
ただ引数には一致するミューテーションを渡せているように思うが、解消されない。
他の要因を探す。
生成された fucntion の引数の改行文字と呼び出し時に指定した引数の改行の位置がズレていたことが原因。
ただこれらはフォーマッターで自動に改行されるものなので困った。
生成されたコードの改行位置のルールが良くわからない。
元にしたミューテーションの改行位置とも微妙に違うので、それに合わせようとするとなかなかに使いづらい。(自動でフォーマットがされる。そもそも読みづらい等々...
オプションとかに生成時のフォーマットのルールとかが指定できないかドキュメントを探ってみる
試しに元となるミューテーションをワンラインにしてみる。
これで生成される関数の引数がどうなるか見てみる。
特に改行の位置に変更はなかった。
おそらく codegen 側の独自の改行ルールがありそう。
ちょっと根本的に使い方が違ってそうな気がしたので、sample を見て理解を深める。
サンプルを見た。
自分との違いは codegen.ts の documents フィールドの設定。
自分の理解では、documents に使用したいクエリがあり、それを素に codegen されると思っているが、サンプルの方で指定しているファイルでは既に codegen されたファイルが import されている。
そもそも codegen しないと import ができない(という理解)ので、順序が逆のように思える。
おそらくここが原因。
自分がやっていること
- 使用するクエリ文字列を任意の *.grphql ファイルに記載。
- documents にそのパスを指定。
- codegen を実行。
- 生成された関数を素に、useMutation を実行しようとする(ここで function が unknown を返す。
サンプルでやっていること
- useMutation, useQuery を App.tsx に記載。クエリも同じファイルに文字列で記載。
- 同じファイルに import 文で生成される gql ディレクトリを指定。
- documents にそのパスを指定。
試しに documents の位置をサンプルに併せて修正してみる。
それで生成されるファイルの差分を見る。そもそも codegen がうまくいくかも怪しい気がする。
codegen は正常に実行された。
function の引数に当てられている改行位置も想定通りの位置で生成されている。
こうなってくると codegen 初回実行のときはどうなるのか。。
gql ディレクトリは生成されていないはずなので、import でコンパイルエラーが出そうなものだけど。。
gql ディレクトリをまるっと消して codegen を実行してみた。
import はもちろんエラーが出ていたが codegen は実行できた。かつ、中身は同じファイルが生成されていた。
このことから考えられるのは、以下のようなこと。
- documents には実際に実行するクエリ文字列の場所を指定する。
- codegen を実行すると、codegenerator その文字列をよしなに読み取って generate してくれる。
- それを import して、クエリ文字列を型安全にし、useQuery の引数に入れる。
サンプルリポジトリはこれらが実行された最終形だから、先に import が定義されているように見える。
graphql-code-generator のドキュメントを読み返す。
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.)
- これは graphql-tag のライブラリにある関数のこと。
import { gql } from '@apollo/client';
const MUTATION_STR = gql`
mutation addHoge(
$lists: [list]
) {
addHoge(
addHoge: $lists
) {
hoge
fuga
}
}
`
- a plain text
- 文字列リテラル。シングルクォートかダブルクォートで囲んだもの。
- $ を使いたい場合はこちらは使えず。バッククォートを使う必要がある。
自分の場合は、graphql-tag を使っていたため、codegen 対象となり生成されていたみたい。
ただやっぱり生成とコーディングの順番が逆なのがしっくりとこない。
- 操作用のクエリ(文字列リテラル)がある
- それを素に codegen。
- codegen を素に apollo client でクエリ発行
という感じが自然な気がしている。
このときの考え方があっている気がする。
参考記事を見つけたので、ここまで理解した知識と組み合わせて修正してみる。
結局困っていたことは、改行の位置が合わないことによる graphql のオーバーロードの失敗。
なのでそこを上手くいくように修正できれば行けそうな気がする。
仮説
- gql-tag を上手く使う
- 生成された hook を上手く使う
useMutation の引数に入れる文字列をシングルクォートにしたらエラーは起きずに解消した。
従来のやり方でだめだった理由は以下みたい
- バッククォートで定義していた。
- そのせいでフォーマッターが聞いて改行や半角スペースがズレた。
ただ今のところ codegen を使用している旨味が感じられていないので、もう少し理解を深める。
codegen で生成されたクエリを引数にした際に生じたメリット
- 実行関数の型定義が any, OperationVariables から具体的な型で定義されていた。
- これによって変数として設定するインプットパラメータが型安全になる。
既存コードを codegen のものに適切に組み込むことができたので、目標は達成できた。
まとめ
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 を使用することができるようになる。