通过GraphQL / Schema Stitching自由地API借用-成为大人吉安
这是 GraphQL Advent Calendar 2018 的第9天。
昨天是ja_rascal的文章。
我是Hiroyuki_OSAKI,GraphQL使用者大约半年。关于GraphQL,我大概已经写了10篇左右的文章了。
本次我们将介绍GraphQL的功能,可实现“复制”,“混合”和“修改”,目的是为了从各种API中借鉴并开发自己的服务,包括“远程模式”,“模式拼接”和“模式转换”。
盗用和改良API (≒仿冒?)
我认为,未来会有越来越多的机会可以(遵守规则地)借鉴和改进其他服务的API。特别是当我们重新利用自身已存在的API来开发新服务时,这个机会往往更为适用,而非借鉴其他公司的API。无论是在公司还是私人事务中,我个人都常说重复利用、再利用。
稍微换个角度,有一个词叫作“骏哲主义”。这个词来源于哆啦A梦中的大雄的名言:“你的东西是我的,我的东西也是我的”。这句话表达了他的生活态度。如果将其解释为API开发方法论,那就是“直接使用已被他人公开的API作为自己的服务,并添加自己的原创元素”。
上面的图片展示了名为”ジャイアン”的服务,实际上它的内容是抄袭的,这是我们想要表达的意思。
为了推进新服务的敏捷开发,也许能否迅速高效地实施敏捷主义可能是关键。
肝脏所在乎的是”复制””混合””修改”.
盗用API。这看起来很简单,但仔细观察会发现有三个操作。
②混ぜるおれのものもおれのもの の部分完全オリジナル
③いじる
请看下面的图片。
这个叫做“加依恩”的服务是本次的主角,它有一些内容(REST API)。
只有“独奏演奏会”是完全原创的内容,其他似乎都是从别人那里拷贝过来的。
-
- 「漫畫」事實上是抄襲了大雄的作品,而「遊戲」則是抄襲了胖虎的作品。
-
- 我們將大雄和胖虎的知識混合在一個服務中。
- 「我的漫畫(大全)」無視大雄的漫畫,隨心所欲地修改(例如:添加自己的評論,刪除原作者信息)。
抄襲的含义是在中文中指的是…
API最初是在原本的大雄和胖虎等其他服务器上运行的,它们具有各自的规格,所以即使说复制也不容易,需要进行这样的操作。在REST API中,可以相对容易地使用像3Scale或Apigee这样的API网关来实现这一点。
-
- リクエストのフォワード
- API仕様の援用
混ぜる的意思是
- パクったAPI同士の仕様の合成
いじる是指调整或改变。
-
- 既存の型に独自のフィールドを追加
- 既存クエリ、型の削除・隠蔽
要实现「复制」、「混合」和「调整」功能,
1. 而第二个恰好是“你的就是我的”。
2. 对于第三个,虽然是他人的,但也是“我的就是我的”。
这种可怕的行为是如何发生的呢?使用GraphQL就很容易实现。
那么,让我们大家一起变成大雄吧。
使用GraphQL的”复制”,”混合”,”修改” 。
我们进入正题。实现复制、混合和编辑的是GraphQL的”远程模式”、”模式拼接”和”模式转换(+委托)”。
基础知识:GraphQL的「API规范自动交互」
只要在GraphQL中使用GraphQL Schema Language(称为“文件”),就可以自动生成并发布服务器的API和文档。
例如,Nobita服务已经定义了以下类型。
"""
Manga API
"""
type Manga {
"漫画の名前"
Name: String
"漫画の巻数"
Volume: Int
"漫画に登場する人物"
Characters: [String]
}
型定義包括规范(名称和类型)和说明文档的一起描述。
将类型定义发送到类似Apollo和GraphQL服务器中,API(请求接收和检查处理等)将自动生成。同时,还会自动生成客户端和服务器之间的API规范文档(称之为Introspection),可以使用__schema查询以获取。
因此,通过这个,可以在服务器和客户端之间进行交流,也可以与其他服务进行交流。
复制API的“远程架构”
现在我们来看看大雄的GraphQL服务器是如何被胖虎复制的。
大雄的GraphQL服务器先前已经公开了自动交互的机制和规格。
要按照这个规格复制API,可以使用graphql-tools中的makeRemoteExecutableSchema(→文档),并指定API的URL来自动获取模式,并将其转换为可在自己的服务中使用的模式。通常会与introspectSchema一起使用,如下所示。
import {makeRemoteExecutableSchema, introspectSchema} from 'graphql-tools';
import fetch from 'node-fetch'
import { HttpLink } from 'apollo-link-http'
// リモートスキーマを作る関数
const createRemoteSchema = async (uri: string) => {
const link = new HttpLink({uri, fetch})
return makeRemoteExecutableSchema({
schema: await introspectSchema(link),
link
});
}
// nobitaのGraphQL APIを使ってリモートスキーマを作っている、つまりパクっている
const nobitaSchema = await createRemoteSchema(
'https://nobita/'
)
在这种情况下,我们会参考公开的 API 文档中的 __schema,从 nobita 中提取规格,并根据它生成可以在 Gian 内部执行的可行架构。
公開一个偷来的API以及启动GraphQL服务器的完成方法如下。
import {graphqlExpress} from 'apollo-server-express'
const app = express();
app.use('/graphql', bodyParser.json(), graphqlExpress({schema: nobitaSchema}));
在中国译文中,”API混合’schema stitching'”可以翻译为 “API 融合 ‘模式缝合'”。
现在,将从大雄和胖虎那里获取的两个API模式混合在一起,这就是模式拼接(Schema stitching)。
可以使用graphql-tools的mergeSchemas(→文档)。
import { mergeSchemas } from 'graphql-tools'
//のび太のスキーマをコピー
const nobitaSchema = await createRemoteSchema('https://nobita/')
//スネ夫のスキーマをコピー
const suneoSchema = await createRemoteSchema('https://suneo/')
//★★ココ★★ ジャイアンのスキーマはのび太のスキーマとスネ夫のスキーマを混ぜて作る
const gianSchema = mergeSchemas({
schemas: [nobitaSchema, suneoSchema],
})
const app = express();
app.use('/graphql', bodyParser.json(), graphqlExpress({schema: gianSchema}));
这个过程的操作大致如下:我们通过使用之前提到的远程架构从哆啦A梦和胖虎获取他们各自的架构,并将它们存入数组中。然后我们将这个数组作为参数传递给mergeSchemas函数,这样就可以自动合成架构。作为服务,无论是漫画还是游戏,都能正常运行API。在生成的API文档中,当然会反映出由哆啦A梦和胖虎创建的文档。
对API进行轻微调整的“架构委派”
如果需要对复制的API进行细微定制,例如「保持相同处理,只需更改一下查询名称」,您可以使用同样的graphql-tools的Schema Delegation(→文档)。在新查询的解析器中使用delegateToSchema,它会将请求转发到指定查询的源服务器URL。
在下面这个API的查询中,原本叫做”manga”的内容完全保持不变,偷偷把名字改成了”orenomanga”。
//自分オリジナルのクエリ名を作る
const linkSchemaDefs = `
extend type Query {
orenomanga: Manga
}
`
const gianSchema = mergeSchemas({
schemas: [nobitaSchema, suneoSchema, linkSchemaDef], //これを追加
resolvers: {
Query: {
orenomanga: { //オリジナルのクエリ名を指定して、処理を記載
resolve: async (parent: any, args: any, context: any, info: any) => {
return info.mergeInfo.delegateToSchema({ //この辺はresolverの決まり文句
schema: nobitaSchema, //実はこのスキーマからパクっている
operation: 'query',
fieldName: 'manga', //パクるクエリ
args: {where: {name: args.name}}, //
context,
info
})
}
},
这个动作是这样的。delegate就是“任给”的意思,虽然orenomanga是一个新的名称,但基本上是把处理委托给了nobitaSchema的manga API。
只需要一种选择,请用中文重新表达以下内容:
深入修改API的”模式转换”
如果您想要完全定制复制的API,例如在删除原有查询时,您可以使用graphql-tools的Schema Transforms(→文档)来实现。
以下的情节表明,由于大雄发表的关于漫画的查询被胖虎偷偷删除了。
const transformedGianSchema = transformSchema(gianSchema, [
new FilterRootFields((operation: string, rootField: string) => {
// falseを返すと消される。
// つまり元あったQuery.mangaはこの文がfalseとなり消される。
return 'Query.manga' !== `${operation}.${rootField}`
}
),
])
在`transformSchema`的第二个参数中,应该传入一个转换方法对象。但不幸的是,这次指定的是`FilterRootFields`,结果哆啦A梦的漫画被删除了。
尽管Schema Transforms仍在不断发展中,但已有一些默认的Transforms,本次我们使用了其中的FilterRootFields。
这一次完成的事情
使用GraphQL的graphql-tools库中的各种功能,可以实现以下这些事情。
graphql-tools
の機能使うかどうか(私の主観)スキーマのコピーmakeRemoteExecutableSchema
+
introspectSchema
結構使うスキーマの合成mergeSchemas
使えるスキーマに変更を加えるtransformSchema
使うかも作为尚未介绍的功能,有Schema Directives。它可以通过身份验证来限制字段的访问权限,还可以做许多其他的事情。
剩下的问题
当你可以复制API时,可能会出现以下问题。
-
- N+1問題
スキーマスティッチングで複数のAPIをパクり合成すると、1回のクエリからパクリ元へのN+1回のリクエストが発生してしまうことがある。解決するにはDataloaderを使ってバッチ化するのがよい。Dataloader自体はGraphQLの共同作者であるFacebookのLee Byronが開発。Dataloaderの考え方はyuku_tさんの5日目の記事に詳しい。
コピーライト問題
問題というわけではないですが、他人のAPIをパクったら、そこに書いてあるコピーライトを読んでちゃんとルールを守りましょうねってことです。例えばcreative commonsのcc-byだったら作者名は記載してあげないといけないわけです。じゃあどこに書くかといったときに、いきなりデータの中に突っ込んだら「あー配列の全要素に作者名が入ってしまってうざいー」みたいなことになるので、メタデータに追記する方法(別記事)を使うといいと思います。
分散によるボトルネック検証
API参照したらなんか遅い。そんな時にはボトルネック検証、となるのですが、さてパクったAPI側なのか自分なのか、パクったAPIのどれなのか、分散してて多少面倒くさいです。そういったときに使いたいのが分散トレーシングなどの技術です。GraphQLと分散トレーシングOpenTracing/Jaegerをくっつけた話はyamitzkyさんの1日目の記事が詳しいです。ちゃんとすべてのAPI提供者がOpenTracing等のなんらかの仕組みに乗る合意ができれば、ボトルネック検証がやりやすくなるのではないかなと思います。ちなみに、サーバ内の処理だけならApolloTracingとかが使えるようです。
总结
使用GraphQL的远程模式和模式合成,可以轻松地复制其他API。如果能熟练使用模式合成,甚至可以复制多个API并将它们合成为一个新的服务。实践Gianism,我们希望不断创造出新的服务。
请查看我之前的两篇文章1. Schema Stitching: 将多个GraphQL API组合成一个新的API,2. Schema Stitching: 使用graphql-binding简单自定义模式,其中提供了实际的示例源代码和操作方法。
这次使用的graphql-tools主要由Apollo等GraphQL创作者们不断追加和更新功能。他们就像哆啦A梦一样慷慨地提供能让大雄、胖虎甚至小夫也像胖虎一样强大的工具。
明天是ubnt_intrepid的日子。