対象は apollo-cli がサポートしてるもので自分の好みとかではない。今回自分が必要だったのは TypeScript。
こういう schema.graphql 持ってるとする。
type Query {
user: User!
}
type User {
id: ID!
name: String!
}
で、これを使うコードを生成したいとして、ファイル構成に色々と前提がある。こんな感じ
schema.graphql
queries/
getUser.graphql
getUser は schema 定義からそれを叩くクエリ
query getUser {
user {
id
name
}
}
query getUser
の部分は単純な graphql query としては省略可能なのだが、型生成で名前をつける都合上必須になっている。
今回の目標は、この schema.graphql を元に getUser.graphql の結果に型を付ける。
apollo-cli でコード生成
ググると apollo-codegen と apollo-cli と apollo が出てきてややこしいが、Issue 見る限りは今後 apollo に集約されるとのことだったので apollo を使う。
npm i -g apollo
(npm i -g apollo-cli だとapolloとは別に apollo-cli コマンドが入る。ややこしい)
apollo codegen:generate がコードを生成するコマンドだが、 *.graphql
を直接食えず、それをコンパイルした json が必要
$ apollo schema:download --endpoint schema.graphql
これで schema.json が生成される。
コマンド名が schema:download なのが気持ち悪いが、 apollo は PaaS 売ってる会社がスポンサーなので、どうもサーバーからスキーマ定義を落とすのが前提にあってこういう定義になってるっぽい。graphqool とかが念頭にありそう。
生成した schema.json を使ってコードを生成する。
$ apollo codegen:generate --target=typescript --schema=schema.json --queries="queries/*.graphql"
実行すると queries/__generated__/getUser.ts
が生成される。
ここで target を scala や swift にするとそれらのコードが生成された。たしかにここまでgraphql の定義だけしか使ってないので、言語に縛られていない。
React + TypeScript で生成された型を使う
react-apollo/Query を使うとする。
まず、この getUser.graphql を webpack 経由で読み込めるようにする必要がある。 yarn add graphql-tag
して、 webpack.config.js の module.rules に .graphql のローダーを追加する。
// module.rules
rules: [
// ...
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: "graphql-tag/loader"
}
]
これで次のような gql タグ付き文字列でクエリを読み込むのと同等の表現になる。
import gql from 'graphql-tag'
export default gql`...`
生成された型を使って、Reactのコードに型をつける。
import { Query } from "react-apollo";
import getUser from "../queries/getUser.graphql";
import { getUser as GetUserType } from "../queries/__generated__/getUser";
const GetUserQuery: React.ComponentType<{
query: any;
children: (p: { loading: boolean; data: GetUserType }) => React.ReactNode;
}> = Query as any;
export default () => {
return (
<GetUserQuery query={getUser}>
{props => {
if (props.loading) {
return "Loading...";
}
return `Hello, ${props.data.user.name} - ${props.data.user.id}`;
}}
</GetUserQuery>
);
};
ちなみに、Query をキャストしてるのは children を介して data に型を付けたいのもあるが、Query がそもそも定義間違ってるのか React.Component の型を満たしていなくて普通に使うだけで型エラーが起きていた…ので握りつぶした。
もうちょい一般化できそうだが、とりあえずこんな感じだと思う。
server 込みで動いてるリポジトリはこちら https://github.com/mizchi-sandbox/graphql-next-ts