GraphQL の Introspection について - schema.json って何だろう
(=˘ ꒳ ˘=) GraphQL を使っているとよく schema.json などと名付けられた JSON 形式のファイルを利用している例に突き当たる...
この schema.json ってなんだろうというお話。
schema.json - Apollo の場合
例えば apollo-codegen の使い方を見てみると
// schema.json 生成 apollo-codegen introspect-schema http://localhost:8080/graphql --output schema.json // schema.json から型定義を生成 apollo-codegen generate **/*.graphql --schema schema.json --output API.swift
のように introspection で得られる値が schema.json ということらしい。
GraphQL の Introspection
Introspection について Working Draft を見ると
__schema: __Schema! __type(name: String!): __Type
で root query から得られるものとある。__Schema
定義は以下の通り。
type __Schema { types: [__Type!]! queryType: __Type! mutationType: __Type directives: [__Directive!]! }
__Type
の中身は GraphQL.js の TypeScript 等の型情報を眺めたことのある人なら「なるほど」と読めるような内容で定義されている。
Standard Introspection Query
apollo-codegen は introspection に
import { introspectionQuery } from 'graphql/utilities'
を利用している。このutilities/introspectionQuery.js には __schema
で得られる introspection 情報を完全に得るためのフィールド定義が延々と書かれている。初めて見た時は目を疑った。
Mocking | GraphQL Tools ではこのクエリは Standard Introspection Query と呼ばれている。しかし Working Draft には見つからなかった。
試しに graphql
で introspectionQuery
を実行してみる。
import { graphql, buildSchema, introspectionQuery } from 'graphql' const schema = buildSchema(` type Query { hello: String! } `) graphql(schema, introspectionQuery).then(console.log)
とすると schema.json でよく見る形式のデータが帰ってくる。
{ data: { __schema: { queryType: [Object], mutationType: null, subscriptionType: null, types: [Array], directives: [Array] } } }
これをさらに buildClientSchema で GraphQLSchema
オブジェクトに戻すことができる。
graphql(schema, introspectionQuery).then(res => { const clientSchema = buildClientSchema(res.data) console.log(clientSchema) })
ドキュメントによると
Build a GraphQLSchema for use by client tools. Given the result of a client running the introspection query, creates and returns a GraphQLSchema instance which can be then used with all GraphQL.js tools, but cannot be used to execute a query, as introspection does not represent the "resolver", "parse" or "serialize" functions or any other server-internal mechanisms.
つまり introspection query の結果を渡すことでクライアントやツール類が利用するための GraphQLSchema
オブジェクトを構成するユーティリティメソッドらしい。Introspection に resolver
などの関数の情報が含まれていないのでこのインスタンスはサーバ側で値を返すために利用することはできない。
GraphQL スキーマの表現形式の比較
ここまで GraphQL 周辺を触ってきて、いろいろなスキーマの表現形式を見かけてきた。
GraphQL Schema Language の DSL を利用したスキーマ表現。JSON形式のスキーマ表現。これは introspection result とも dump と呼ばれることも多い気がする。それから GraphQLSchema
オブジェクトで表される JavaScript コード中の表現。折角なので比較してみよう。
GraphQL Schema Language (.graphql
)
description
やカスタムディレクティブ定義など一部の表現をサポートしていない- 人が読み書きしやすい
GraphQL.js の GraphQLSchema
Introspection Result (.json
)
- Introspection の結果をそのまま JSON として吐き出したもの
- 雰囲気としてはGraphQLSchema から JSON として表現できない処理を除いたあらゆる情報が含まれている形式
- 情報としては完全なものに近いが、しかし人が読み書きするものではない
どれも一長一短だが、ワークフロー中のスキーマ取得手法として「APIサーバからの型情報取得」という手を採用するのなら現状 GraphQL Schema Language では表現しきれない部分も多いため Introspection Result を型情報のソースとしておいておくのは妥当に思える。
Swagger や同様、スキーマというものはそれ単体では機能しない。必ず「ワークフロー」と呼ばれるスキーマ共有・更新の共有を行うための仕組みが必要となる。GraphQL の場合は Introspection の仕組みがそれに該当するが、Custom Scalar をサーバ・フロントでどう共有するかなど、まだ掴みきれていない部分も多い。ライブラリとしてリポジトリ管理し両サイドで共有するのがいいか、それとも他に何か良い方法があるのか...