-
-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GraphQL 中 resolver 的两种写法 #80
Comments
请问作者的 ER 图是如何制作的? |
大佬,不知道现在 |
@MBearo 社区现有的DB Datasource: https://www.apollographql.com/docs/apollo-server/data/data-sources/#community-data-sources 根据自己项目用到的db driver,不论是原始的db driver,比如node-postgres,还是knex.js这样的query builder, 还是sequelize, typeorm这样的orm,自己撸一个。 看一下源码:https://github.com/cvburgess/SQLDataSource/blob/master/index.js 几个重要的点:
关于缓存模式的更多信息,看微软的这个技术架构指南cache-aside
|
跪谢大佬,容我慢慢研究研究,感谢!!! |
GraphQL 中 resolver 的两种写法
基础知识略过,直接进入正题,下面要介绍的
resolver
的两种写法直接关系到性能问题。先来看下示例的
GraphQL
SDL的ERD:很常见的数据关系模型,User和Post是一对多的关系,数据库表ERD如下:
GraphQL
SDL如下:示例使用
memoryDB
内存数据库模拟RDBMS:接下来编写
resolver
,有两种写法,我们来对比和分析一下。在一个resolver中解析所有SDL定义的字段
resolver
如下:可以看到,
postById
这个resolver
在数据库中根据id
去posts
表中查询post
,查询出post
后,还根据post
实体上的postAuthorId
这个外键去users
表中查询相应的user
。同理,posts
这个resolver
查询出所有的post
实体后,遍历所有post
实体,根据postAuthorId
外键去users
表中查询每个post
实体对应的user
。接下来我们在
GraphQL
Playground中编写GraphQL
客户端查询postById
这个query
查询了Post
这个Object type
上的所有字段,客户端取得正常返回结果。删除
postAuthor
这个查询字段,客户端也取得正常返回结果。从客户端的角度来看,上述两次查询都取得了正常结果,然而,上述这样编写
resolver
,会让服务端多出很多不必要的字段解析,甚至严重影响应用程序性能。我们继续看第二次查询,客户端并没有查询postAuthor
这个字段,但是postById
这个resolver
却依旧去数据库中查询了user
作为postAuthor
,这浪费了一次查询,占用了一个连接池资源。可以试想,如果postAuthor
是通过Http
请求去某个远程服务器上获取的,这一次http
请求也是一次无意义的性能损耗。同理,客户端查询
posts
,但如果不查询postAuthor
,然而服务端的resolver
却给每一个post
去查询相应的postAuthor
,这就是很多次的无意义的查询。所以下面介绍官方正确的resolver
写法:正确的resolver写法
再次执行之前的两次客户端查询
postById
,客户端不查询postAuthor
,那么postById
这个resolver
只进行了一次数据库查询,根据id
去posts
表中查询相应的post
。客户端查询
postAuthor
,此时,服务端的Post.postAuthor
这个resolver
才会执行,从而去users
表中查询相应的user
作为postAuthor
。下图是客户端查询postAuthor
的返回结果:服务端
Post.postAuthor
这个resolver
执行后打印出的console.count
日志如下:这个问题想出来的比较早,有兴趣的可以看看原始提问:
https://stackoverflow.com/questions/52272727/resolve-all-fields-in-one-resolver-v-s-resolve-field-in-each-resolver
使用一年多下来,有时候觉得
GraphQL
并不严谨,很多问题官方没有解释的很清楚,比如SDL中定义循环引用的问题:https://stackoverflow.com/questions/53863934/is-graphql-schema-circular-reference-an-anti-pattern。
以及SDL如何设计,
Object
Type
嵌套层级深度,客户端查询字段如何映射到SQL查询column(SQL语句中查询的column和客户端查询的字段一一对应,不多查column)的问题。此外还需要解决N+1
query,缓存问题。apollographql
的一些GraphQL
模块实现也还处于起步阶段,apollographql
的服务端上传模块apollo-upload-server和客户端上传模块apollo-upload-client相比于multer和jQuery-File-Upload,弱太多了,只能满足最基本的上传需求,所以如果对上传功能要求比较高的话,建议使用现有更成熟的方案。对于apollographql
升级到2.0以后,使用的dataSource
,目前官方也只给出了HTTP
通信的dataSource
实现apollo-datasource-rest,SQL和NoSQL的dataSource
实现社区里都是一些个人零零散散的实现,对于新手来说,基本等于没有。如果你使用
TypeScript
,Node.js
,GraphQL
作为技术栈,推荐一个库: https://typegraphql.ml/, 能减少很多冗余代码。源码
https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/resolve-fields-in-one-resolver
The text was updated successfully, but these errors were encountered: