通过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作为自己的服务,并添加自己的原创元素”。

image.png

上面的图片展示了名为”ジャイアン”的服务,实际上它的内容是抄袭的,这是我们想要表达的意思。

image.png

为了推进新服务的敏捷开发,也许能否迅速高效地实施敏捷主义可能是关键。

肝脏所在乎的是”复制””混合””修改”.

盗用API。这看起来很简单,但仔细观察会发现有三个操作。

ジャイアンでいうとAPIでいうとおまえのものはおれのもの の部分①コピる
②混ぜるおれのものもおれのもの  の部分完全オリジナル
③いじる

请看下面的图片。
这个叫做“加依恩”的服务是本次的主角,它有一些内容(REST API)。
只有“独奏演奏会”是完全原创的内容,其他似乎都是从别人那里拷贝过来的。

    1. 「漫畫」事實上是抄襲了大雄的作品,而「遊戲」則是抄襲了胖虎的作品。

 

    1. 我們將大雄和胖虎的知識混合在一個服務中。

 

    「我的漫畫(大全)」無視大雄的漫畫,隨心所欲地修改(例如:添加自己的評論,刪除原作者信息)。
image.png

抄襲的含义是在中文中指的是…

API最初是在原本的大雄和胖虎等其他服务器上运行的,它们具有各自的规格,所以即使说复制也不容易,需要进行这样的操作。在REST API中,可以相对容易地使用像3Scale或Apigee这样的API网关来实现这一点。

    • リクエストのフォワード

 

    API仕様の援用

混ぜる的意思是

    パクったAPI同士の仕様の合成

いじる是指调整或改变。

    • 既存の型に独自のフィールドを追加

 

    既存クエリ、型の削除・隠蔽

要实现「复制」、「混合」和「调整」功能,

1. 而第二个恰好是“你的就是我的”。
2. 对于第三个,虽然是他人的,但也是“我的就是我的”。
这种可怕的行为是如何发生的呢?使用GraphQL就很容易实现。

那么,让我们大家一起变成大雄吧。

使用GraphQL的”复制”,”混合”,”修改” 。

我们进入正题。实现复制、混合和编辑的是GraphQL的”远程模式”、”模式拼接”和”模式转换(+委托)”。

image.png

基础知识:GraphQL的「API规范自动交互」

只要在GraphQL中使用GraphQL Schema Language(称为“文件”),就可以自动生成并发布服务器的API和文档。
例如,Nobita服务已经定义了以下类型。

"""
Manga API
"""
type Manga {
    "漫画の名前"
    Name: String
    "漫画の巻数"
    Volume: Int
    "漫画に登場する人物"
    Characters: [String]
}

型定義包括规范(名称和类型)和说明文档的一起描述。
将类型定义发送到类似Apollo和GraphQL服务器中,API(请求接收和检查处理等)将自动生成。同时,还会自动生成客户端和服务器之间的API规范文档(称之为Introspection),可以使用__schema查询以获取。

image.png

因此,通过这个,可以在服务器和客户端之间进行交流,也可以与其他服务进行交流。

复制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 内部执行的可行架构。

image.png

公開一个偷来的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梦和胖虎创建的文档。

image.png

对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。

image.png

只需要一种选择,请用中文重新表达以下内容:

深入修改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梦的漫画被删除了。

image.png

尽管Schema Transforms仍在不断发展中,但已有一些默认的Transforms,本次我们使用了其中的FilterRootFields。

Schema Transformsの名前機能使うかどうか(私の主観)FilterTypesTypeを抹消使うかもRenameTypesTypeの名前を変更結構使うかもTransformRootFieldsRootField(Query, Mutation, Subscriptionのどれか)に変更を加える難しくて使わないFilterRootFieldsFieldを抹消結構使うRenameRootFieldsFieldの名前を変更結構使うExtractFieldパスを指定したパスに変更よく分からない、使わないWrapQuery何でもできそうな変更処理難しすぎて使えない

这一次完成的事情

使用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的日子。

广告
将在 10 秒后关闭
bannerAds