graphql-codegen を用いた GraphQL, TypeScript, React-Hooks の連携
Tweet実際のコード
https://github.com/superyusuke/react-typescript-apollo-bff-codegen
youtube へのリンク
GraphQL Codegen を用いて GraphQL のコードから型安全な TypeScript 開発に必要なコードを生成できる
Sever 側
- Schema に対応した TypeScript の型を生成する
- Resovler の型を Schema から生成する
- Server の Context の型を生成する(Apollo Server を立てるときに外から注入した DB や、Apollo Sever への request に含まれる header 情報等のこと。context は resolver から参照できる)
- 独自に定義した Scalar = 型情報 に対応する TypeScript の型を生成する
Client 側
- Schema に対応した TypeScript の型を生成する
- typescript-operations: .tsx に書いた query, mutation 等から対応する型を生成する
- typescript-react-apollo: .tsx に書いた query, mutation 等から対応する hooks を生成する
Server 側の環境設定
パッケージのインストール
# codegen に必要なパッケージの追加
yarn add @graphql-codegen/cli @graphql-codegen @graphql-codegen/typescript @graphql-codegen/typescript-resolvers @graphql-codegen/introspection
codegen の設定ファイルを書く
# codegen.yml
overwrite: true
generates:
# GraphQL の Resolver に必要な型をつける
# Resolver は実際に値を DB から取得したり API を使用する部分
./src/types/generated/graphql.ts:
# Schema の場所を指定
schema: ./src/graphModules/*/index.ts
config:
useIndexSignature: true
# context:
# Apollo Server を立てるときに外から注入した DB や、
# Apollo Sever への request に含まれる header 情報等のこと
# context は resolver から参照できる
contextType: ../context#Context
# scalars: Apollo Server がデフォルトで持っている以外の
# 基本的な型を定義することができる
scalars:
# 主に画像アップロード時に必要になる型
Upload: ../scalars#Upload
# codegen は plugin を用いて適切な code を生成する
plugins:
- typescript # typescript に出力するために必要
- typescript-resolvers # graphQL の Resolver のための型にするために必要
# これはなくても多分いい
# IDE で補完させるために自分は作っている / サーバーサイドではいらないかも
./graphql.schema.json:
schema: ./src/graphModules/*/index.ts
config:
useIndexSignature: true
contextType: ../context#Context
scalars:
Upload: ../scalars#Upload
plugins:
- introspection
npm コマンドを実行して書き出す
"codegen-server:watch": "gql-gen --watch"
: 監視
"codegen-server": "gql-gen"
: 一度きり
何が書き出されるか
Schema に対応した TypeScript の型を生成する
例えば以下の GraphQL の schema 定義は、そのままでは TypeScript からは参照できない。これに対応する TtypeScript の型定義を生成する必要がある。
type Item {
id: Int!
name: String!
}
上記 schema から以下の TS 型定義を生成することができる。
export type Item = {
__typename?: 'Item';
id: Scalars['Int'];
name: Scalars['String'];
};
Resovler の型を Schema から生成する
Resolver は実際に値を DB から取得したり API を使用する定義を書く部分。TypeScript でかく。この GraphQL の Resolver に必要な型を生成する。
// schema
export const typeDefs = gql`
type Query {
_dummy: Boolean
healthCheckMessage: String!
}
type Mutation {
_dummy: Boolean
}
type Subscription {
_dummy: Boolean
}
`;
// schema から生成された Resovlers という型を Resolver につけることで型がわかる
export const resolvers: Resolvers = {
Query: {
healthCheckMessage: async () => {
await delay(1000);
return "this is health check message. OK";
},
},
};
Resolver の型をつけたことで、おかしければエラーになる。
export const resolvers: Resolvers = {
Query: {
// 誤った operation 名を定義すると TS 型エラーになる
AAAAAAhealthCheckMessage: async () => {
await delay(1000);
// 文字列を返すべき場所で number を返すと、型エラーになる
return 999999;
},
},
};
Server の Context の型を生成する
Server の Context の型を生成する(Apollo Server を立てるときに外から注入した DB や、Apollo Sever への request に含まれる header 情報等のこと。context は resolver から参照できる)
まず context の型を定義する。
type Token = string;
type Item = {
name: string,
id: number}
type DB = {
items: Item[]
currentId: number
}
export type Context = {
token: Token;
db: DB
};
設定ファイルで context.d.ts を参照する。この参照は「生成された graphql.ts から見て相対パスでどこにあるか」を指定する。少しこんがらがる。
# codegen.yml
overwrite: true
generates:
./src/types/generated/graphql.ts:
schema: ./src/graphModules/*/index.ts
config:
useIndexSignature: true
contextType: ../context#Context ## ここで context.d.ts を参照させる
scalars:
Upload: ../scalars#Upload
plugins:
- typescript
- typescript-resolvers
すると resolver の context に型がつき、token や db がくることがわかる。これをしないと、any になってしまう。
独自に定義した Scalar = 型情報 に対応する TypeScript の型を生成する
import { FileUpload } from 'graphql-upload';
export type Upload = Promise<FileUpload>;
# codegen.yml
overwrite: true
generates:
./src/types/generated/graphql.ts:
schema: ./src/graphModules/*/index.ts
config:
useIndexSignature: true
contextType: ../context#Context
scalars:
Upload: ../scalars#Upload # 独自定義した scalar を読み込ませる
plugins:
- typescript
- typescript-resolvers
Client 側
Operation = Query, Mutation 等を gql タグで書いたら、そのための hooks が生成される。
schema: http://localhost:8080/graphql
documents:
- ./src/**/*.tsx
- ./src/**/*.ts
overwrite: true
generates:
./src/types/generated/graphql.ts:
plugins:
- typescript
- typescript-operations # .tsx に書いた query, mutation 等から対応する型を生成する
- typescript-react-apollo # .tsx に書いた query, mutation 等から対応する hooks を生成する
config:
skipTypename: false
withHooks: true
withHOC: false
withComponent: false
./graphql.schema.json:
plugins:
- introspection
以下のように書くだけで、それを使うための react-hooks とそれによって取得できる型が生成される。
gql`
query blar {...}
`
まとめ
React, TypeScript, GraphQL による型安全なアプリケーション開発は、実はほとんと GraphQL Codegen のおかげである。開発に必要になる GoraphQL Codegen の設定を全て紹介した。TypeScript による高速堅牢な開発を推進してほしい。