超图
如果我们重新审视 API 的作用,我们会发现,作为客户端和服务端数据的桥梁,API 解析客户端的请求,从服务端某个数据源(可能是数据库,也可能是其他服务的数据等),获取相应的数据,然后按照 API 的约定返回合适的结果。
既然 API 的目的是提供数据,而数据往往有其严苛的 schema,同时 API 的 schema 大多数时候就是数据 schema 的子集,那么,我们是不是可以从数据 schema 出发,反向生成 API 呢?
但不同数据源协议不同,如 对数据库而言,不同数据库有不同SQL方言,对REST api和gql api协议也不同。
而且聚合不同类型数据源的数据,也是API的常见用例。因此,需要先将不同协议的schema,统一为一种协议,然后再行编排,生成API,才能符合绝大多数应用场景。
统一协议
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-ac323dc42c823c5de64c2d6f5e6841fa129c488e%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=b917622c&sv=1)
gql作为一种强类型API语言,非常适合作为该统一协议。
借助Prisma引擎,将不同类型数据库的schma转成了gql schema,统一了不同数据库的sql方言。
同时,利用openapi specification规范,将rest api也转成了gql schema
而graphql api,本身就是gql协议,具备gql schema
我们将上述各数据源转换而来的GQL SCHEMA,称为子图。
各个子图合并后的产物称为超图,它本质上也是GQL SCHEMA,只不过聚合了不同的数据源。图中将3种类型数据源 数据库、REST API以及gql api产生的超图,都合并到了这个超图中。
但为了避免不同数据源相同表名或接口合并到超图后,产生冲突,因此需要给不同子图加上命名空间,一示区分。
上述所有的步骤都是自动执行的,无需手工编写gql schema,极大提升开发体验。
而实现上述自动化的核心需要依赖gql的内省能力。
Prisma
接着我们学习下prisma mode到gql scheama的转换规则。
prisma的核心原理见 数据库
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-f9c0713c108b740a160e8fad2d2bfcf78df0998b%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=86da496f&sv=1)
首先,prisma model与数据库建表语句等价。且prisma model能转换成多种数据库的建表语句。
例如,这里的prisma model转换为了pgsql的建表语句。
接着,我们看下prisma model到gql schema的转换规则。
这里有一个todo模型,右侧是其转换后的schema可视化展示。
规则为:命名空间+方法名+表名。命名空间是为了区分不同数据库实例的相同表名。
其中查询有如下方法:
findFirst:返回列表中符合条件的第一条记录
findMany:返回记录列表
findUnique:根据主键或唯一键查询一条记录
aggregate:聚合数据,包括 avg、count、sum、min、max
groupBy:聚合函数,根据一或多列对结果集分组
变更有如下方法:
createOne:创建一条记录,对应 sql 中的 insert
createMany:创建多条记录
deleteMany:批量删除记录,对应 sql 中的 delete
deleteOne:删除一条记录,对应 sql 中的 delete
updateMany:更新多条记录,对应 sql 的 update
updateOne:更新一条记录,对应 sql 的 update
upsertOne:更新或插入一条记录
里面还包含两个原生方法,用于编写复杂SQL,适用于上述方法无法覆盖的场景。
queryRaw:执行查询类sql
executeRaw:执行创建、变更类sql
对于有关联关系的模型,其函数名称一致,但入参会更加复杂,这块需要前往prisma官方文档查看。
Prisma对不同数据库的函数支持是不同的,例如sqlite数据库缺少createMany方法。
RSET API
飞布除了支持数据库数据源外,还支持REST 数据源。
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-c9252732e6aae32327a43b3eaff43b73ca51f8c8%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=483ed28a&sv=1)
如图所示,是OAS3.0规范的示例,可以看到它也定义了入参和出参,并且支持类型,可以转换为gql scheme。
所以只需要提供OAS文件,飞布就能将rest api转变为子图,然后合并到飞布的超图中。
注意看,子图没有命名空间前缀,超图中增加了前缀PET。
接着,我们看看OAS生成GQL SCHEMA子图的详细规则:
paths中请求类型对应gql schema中的query:get对应query,POST/put/patch对应mutation。例如,pet/{petid} get 转换为query类型。
paths的operationid,对应gql schemla 中的根字段名称,或者说函数名称,如果它为空,则用path去除特殊字符后函数名。例如getpetbyid。
parameters入参对应gql schemla 中函数的入参。例如,petid为integer类型,对应到gql schema为Petid int类型,required true,对应为!.
response出参对应gql schemla 中函数的返回值。例如,pet schema转换为pet 类型。id字段为标量interge类型,转换为int。category为对象,在这里也是对应为cateory对象。
推荐使用apifox等工具导出oas文件,优先导出为oas3.0。
架构图
最后我们查看下架构图,重点关注超图部分。
![](https://docs.fireboom.io/~gitbook/image?url=https%3A%2F%2F2514536170-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FhvHBEKUXoqt1bL00q2Ra%252Fuploads%252Fgit-blob-db7d4322bf87b022d8c192cc21cb3febb5a01fcc%252Fimage.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=f421f4dc&sv=1)
在这里,飞布主要做了2件事:
用graphql统一协议,屏蔽数据库、REST API等不同类型数据源的协议。其中又用prisma屏蔽了各数据库sql方言的差异。
自动内省自行转换协议:将rest api的oas文件转换为graphql,自动内省数据库和graphql 数据源,避免手动编写graphql。
至此,你已经掌握了飞布最核心的概念——超图。
最后更新于