4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Schema Stitching: graphql-bindingで簡単にちょっとスキーマをカスタマイズする

Last updated at Posted at 2018-08-11

何のため?

前回Schema Stitching: 複数GraphQL APIをくっつけて新しいAPIを作ろうにて、スキーマスティッチングの基本・最低限の実装をしたわけですが、最低限実装では自由度が低くてなかなか使い物になりません

自由度が低いところ

  • 型の変更が難しい
    • 既存APIの出力が配列型だとスティッチング結果もそのまま配列型になってしまう
  • スキーマの変更が難しい
    • delegateToSchemaを単純に使うだけでは既存APIと同じスキーマになってしまう

graphql-bindingでちょっとカスタマイズしてみる

非常に簡単にできます。

実装方法概要

  • 定義の変更は**スキーマでextend**を使う
  • 出力処理の変更はBindingかSchemaTransformを使う(今回はBindingを活用)
    • locationAPIのスキーマをBindingに入力してbindingオブジェクトを作成
    • bindingオブジェクトの**queryを呼び出し、取得locationsオブジェクトを直接操作**

実装

ソースコードは、前回実装したものに一部追加する。

  • 型を変える
    • 【変更前】既存API locationsの型が[Location]という配列
      →【変更後】新APIではその要素1つを取り出し、型をLocationとする
  • スキーマを変える
    • Locationスキーマに新フィールドlocation_typeを追加する

usersの内部を以下のように変更する。

  • 旧フィールドlocationsをスキーマ変更した新フィールドlocationを作る
  • locationは、locationsの配列から0番目の要素を取り出して表示する
  • さらに既存locationsAPIにはない新フィールドlocation_typeを追加し、値を"LARGE_CITY"を入力する
import { GraphQLServer } from 'graphql-yoga'
import { makeRemoteExecutableSchema, mergeSchemas, introspectSchema } from 'graphql-tools'
import { createHttpLink } from 'apollo-link-http'
import { Binding } from 'graphql-binding' // ★★追加
import fetch from 'node-fetch'
import { config } from 'dotenv'
config()'
const __API_PORT__ = process.env.API_PORT

async function run() {
	const createRemoteSchema = async (uri: string) => {
		const link = createHttpLink({uri, fetch})
		return makeRemoteExecutableSchema({
			schema: await introspectSchema(link),
			link,
		});
	}
		
	const userSchema = await createRemoteSchema('http://localhost:4020')
	const locationSchema = await createRemoteSchema('http://localhost:4021')
	const linkSchemaDefs = `
		extend type User {
			# original
			locations: [Location],
			# new api (TYPE CHANGE: from array to single object) ★追加
			location: Location
		}
		# ★追加
		extend type Location {
			# SCHEMA CHANGE: field added 
			location_type: String
		}
	`

	// ★★追加
	class LocationBinding extends Binding {
		constructor() {
			super({ schema: locationSchema })
		}
	}
	const locationBinding = new LocationBinding()

	const schema = mergeSchemas({
		schemas: [userSchema, locationSchema, linkSchemaDefs],
		resolvers: {
			User: {
				// original
				locations: {
					fragment: `fragment LocationFragment on Location {address}`,
					resolve: async (parent: any, args: any, context: any, info: any) => {
						return info.mergeInfo.delegateToSchema({
							schema: locationSchema,
							operation: 'query',
							fieldName: 'locations',
							args: {where: {address: parent.address}},
							context,
							info
						})
					}
				},
				// new api ★★追加
				location: {
					fragment: `fragment LocationFragment on Location {address}`,
					resolve: async (parent: any, args: any, context: any, info: any) => {
						let locations = await locationBinding.query.locations({where: {address: parent.address}}, info)
						return {
							...locations[0],
							// add new field
							location_type: "LARGE-CITY"
						}
					}
				}
			}
		}
	})

	const server = new GraphQLServer({ schema	})
	server.start({port: __API_PORT__}, () =>
		console.log(`Your GraphQL server is running now ...`),
	)
}

run()

結果

usersに対してクエリを打った結果。locationを含んでおり、これまでになかったフィールドlocation_typeが含まれている。

image.png

気を付けること

  • locations[0]で値を取り出して、それを拡張する。...Spread Operatorといい、既存オブジェクトの要素をすべて含む、という意味だそう。

感想

これらのツールを作ったApolloとprismaは、本当に優れたアーキテクチャの概念・ツール群を提供してくれていると思う。こんなに簡単にできるとはびっくり。

参考

4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?