こんにちは。とろろかけ牛丼が食べたい亀山です。
先日弊社 Web サイトを Wordpress から Wordpress をバックエンドとして用いた Next.js への移行を行いました。移行の際に導入した GraphQL と React をつかったワークフローが未体験で面白かったので、社内へ展開するためハンズオン形式でプチ勉強会を行いました。本記事ではその時に使った資料を記事として共有致します。
GraphQL とは
- REST API のようにサーバとクライアントでデータをやり取りできるもの
- REST API と違って、リクエストする側でレスポンスに含める情報を指定できる
- 必要なものだけ取得できるので無駄がない
- どんな型のどんなデータが取れるかというスキーマを取得できる <- 重要
見てみよう
- GitHub の API があるよ
- GraphiQL という GUI ツールがデファクト
- スキーマをポチポチ見たり、クエリを GUI で作れる
React から取得してみよう
- JavaScript のサンプルプロジェクト
ハンズオン
- クエリを書き換えてスター数 (stargazerCount) を表示してみよう
- GraphiQL でクエリを作ろう
- コンポーネントを書き換えよう
型をつけよう
- TypeScript のサンプルプロジェクト
ハンズオン TypeScript
- クエリを書き換えてスター数 (stargazerCount) を表示してみよう
- さっきのクエリを入れてみる
- 保存するとコード生成が走り、型が付きます
- コンポーネントを書き換えよう
面白いところ
- TypeScript のコードと GraphQL のスキーマを統合した型定義が生成される
- 基本的にはコード生成でエラーが出なければ動くクエリになっている
- コード生成するのに参照した TypeScript のソースファイルの中で生成された型定義を使うところ
- スキーマから静的な型定義が生成されて、それを利用するだけという一方方向の使い方ではなく、コードからも型定義を生成するというアクロバティックな構成
- Swagger とかを理解していると逆にしっくりこないかもしれない
- クライアントサイドで手書きで
string
などの具体的な型指定を行わない世界観- API から提供されたスキーマがもとになるので、number だっけ?string だっけ?optional だっけ?みたいな迷いや間違いがない
Fragment とは
- クエリから受け取るレスポンスの形式の指定だけを抽出したもの
- 色々なクエリで再利用できる
ハンズオン: Fragment を定義しよう
- さっきのクエリを Fragment に分けてみよう
gql(` fragment RepositoryData on Repository { id name } `) const GET_REPOSITORIES = gql(` query GetRepositories { search(query: "swift", type: REPOSITORY, first: 10) { nodes { ...RepositoryData } } } `)
ハンズオン: Fragment Colocation してみよう
- リポジトリ一つを表示するコンポーネントを作ろう
- コンポーネント側に Fragment を移動しよう
- Fragment をマージしてみよう
コンポーネント
Fragment の型は <Fragment名>Fragment
として生成されます。
export interface RepositoryRowProps { repository: RepositoryDataFragment } export function RepositoryRow({ repository }: RepositoryRowProps) { return ( <div key={repository.id}> <p>{repository.name}</p> </div> ) }
Fragment Colocation の嬉しいところ
- View は気軽に変わるのでレスポンスに必要なデータが何かちゃんと管理するのは難しい
- Fragment Colocation を実施すると、View 側で必要なデータを定義できるので過不足なく指定できる
- 型定義はスキーマから生成しているので、コンパイルが通れば正しいクエリを書いている保証になる
ハンズオン: RepositoryRow をリッチにしよう
リポジトリの作成日や、作者のユーザーアイコンなどを追加してみよう
このとき、RepositoryList やそのクエリを変更することなく、RepositoryRow 内の変更だけで済む喜びを味わってください。
注意点
gql
は./__generated__/graphql
から import してください。@apollo/client
から import すると型がつきません- fragment などを書き換えた後に VSCode 上で TypeScript のコードにエラーがある状態になることがあります。コード生成が終わったタイミングでもう一度保存すると解消されます
どうやって型つけされているか
- gql 関数はデフォルトでは string を引数にとるものしか無いが、コード生成によって、特定のクエリの文字列を引数に取る関数がオーバーロードされている
export function gql(source: string): unknown; export function gql(source: "\n query GetRepositories {\n search(query: \"swift\", type: REPOSITORY, first: 10) {\n nodes {\n ...RepositoryRow_repository\n }\n }\n }\n"): (typeof documents)["\n query GetRepositories {\n search(query: \"swift\", type: REPOSITORY, first: 10) {\n nodes {\n ...RepositoryRow_repository\n }\n }\n }\n"];