LoginSignup
38
32

More than 5 years have passed since last update.

apollo-cli で GraphQL Schema から TypeScript/Flow/Scala/Swift のコードを生成する

Last updated at Posted at 2018-07-06

対象は apollo-cli がサポートしてるもので自分の好みとかではない。今回自分が必要だったのは TypeScript。

こういう schema.graphql 持ってるとする。

schema.graphql
type Query {
  user: User!
}

type User {
  id: ID!
  name: String!
}

で、これを使うコードを生成したいとして、ファイル構成に色々と前提がある。こんな感じ

schema.graphql
queries/
  getUser.graphql

getUser は schema 定義からそれを叩くクエリ

queries/getUser.graphql
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 のローダーを追加する。

webpack.config.js
  // module.rules
  rules: [
    // ...
    {
      test: /\.(graphql|gql)$/,
      exclude: /node_modules/,
      loader: "graphql-tag/loader"
    }
  ]

これで次のような gql タグ付き文字列でクエリを読み込むのと同等の表現になる。

import gql from 'graphql-tag'
export default gql`...`

生成された型を使って、Reactのコードに型をつける。

pages/index.ts
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

38
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
32