那么如果用GraphQL的方式呢?
query shareDetailPage($shareId: Int!, $creatorId:ID!, $start: Int!, $limit: Int = 10) { # 分享详情 shareDetail: share (shareId: $shareId) { shareId: id title desc where logoUrl attchments } # 评论信息 commentInfo(shareId: $shareId, start: $start, limit: $limit) { totalCount comments { id userId content commentTime } } # TA的分享 hisShares (creatorId: $creatorId) { shares { title desc where startTime } } }
一个查询即可搞定。
mutation操作
变更操作,这里只介绍一种场景。到了分享详情页,我们可能会需要编辑这个分享,在传统的方式中,需要调一个更新操作的接口:
POST /api/share/update/:shareId FormData: title=xxx&desc=xxx&where=xxx
调完此接口后为了确认确实已经更新成功了,我们可能还会调一次获取分享详情接口:
GET /api/share/:shareId
接下来我们换成GraphQL的方式:
mutation editShareInfo($shareObj: ShareInput!) { editShareInfo(shareInfo: $shareObj) { shareId: id title desc where logoUrl attchments } }
这样,便可以直接将分享内容修改并返回修改后的分享详情
其他的功能
为了我们写查询语句部分代码能有更好的可复用性,GraphQL
还提供了Fragments
(片段), Inline Fragments
(内联片段)和Directives
(指令)功能。前两者可以类比为JavaScript中的function
(函数)和anonymous function
(匿名函数),Directives
(指令)可以根据我们传的参数来决定某些字段是否需要返回。这里就不做过多介绍了。
以上的功能如何实现?
schema
通过上面的例子,肯定会产生些疑问,我们要如何知道可以查询哪些字段?使用哪些参数?这就需要引入schema
了。
通俗点说,schema
就是协议,规范,或者可以当他是接口文档。
GraphQL规定,每一个schema
有一个根(root)query和根(root)mutation。
我们先来看Root Query怎么写,依然是上面的查询的例子
# 定义一个根查询 type Query { # 可以查询的字段和参数 shares(start: Int = 0, limit: Int = 10, creatorId: ID): [Share!]! share(shareId: ID!): Share! commentInfo(shareId: ID!, start: Int = 0, limit: Int = 10): CommentInfo! }
数据类型
如果你熟悉TypeScript或Flow的话可能会发现上面的写法似曾相识,是的,里面的含义就是你想的那样。每一个可以查询的字段的参数后面会跟标明这个参数的类型,!
用来表示这个参数不可以是空的。[]
表示查询这个字段返回的是数组,[]
里面是数组的类型。
上面我们还看到了一些在TypeScript中不存在的类型,比如ID
,ID
我们暂且把他当成字符串String
类型就可以了。类似我们熟悉的JavaScrpit或TypeScript,GraphQL
也有几个基础类型,在GraphQL
中他们统称叫标量类型
(Scalar Type),主要包括:Int(整型), Float(浮点型), String(字符串), Boolean(布尔型)和ID(唯一标识符类型)。同时,GraphQL
也允许你去自定义标量类型,例如:Date类型,只需实现相关的序列化,反序列化和验证的功能即可。
对象类型
上面的根查询定义中,我们还看到了一些与业务相关的类型,比如Share, Comment,这些统称为对象类型
。对象类型也是GraphQL
中的schema
的基本组件,他可以告诉我们在服务上可以获得到哪些对象,以及这个对象有哪些字段。接下来我们要做的就是定义这些对象类型,直到全部为基础类型。
# 定义Share的对象类型 type Share { id: ID! title: String! desc: String! startTime: Int! where: String attchments: String logoUrl: String creatorId: ID! lastUpdateTime: Int is_delete: Int score: Int createTime: Int! } # 定义评论信息对象类型 type CommentInfo { totalCount: Int! comments: [Comment!]! } # 定义评论对象类型 type Comment { id: ID! content: String! commentTime: Int! userId: ID! shareId: ID! }
这样,我们就完成了schema的定义。
其他类型和功能
GraphQL
其实还有Enumeration types
(枚举类型),Union types
(联合类型)。同时,为了代码能更好的复用,GraphQL
还提供了 Interface
(接口)功能。这里就不做过多介绍了。
实现执行
GraphQL
约定,我们需要为Root Query(根查询)和Root Mutation(根变更)里面的每一个字段提供一个resolver
的函数。并包装成一个对象暴露出去,就像这样:
const resolvers = { // 这里面写查询操作字段的resolver函数 Query: {}, // 这里面写变更操作字段的resolver函数 Mutation: {}, } export default resolvers
让我们继续写完整:
// 一些加载数据的async function import { loadSharesFromDB, loadShareById, loadCommentsByShareId } from './datasource' const resolvers = { // 这里面写查询操作字段的resolver函数 Query: { shares: (parent, { start, limit, creatorId }, context, info) => { return loadSharesFromDB(start, limit, creatorId) .then(...) }, share: (parent, { shareId }, context, info) => { return loadShareById(shareId) .then(...) }, commentInfo: (parent, { shareId, start, limit }, context, info) => { return loadCommentsByShareId(shareId, start, limit) .then(...) }, }, // 这里面写变更操作字段的resolver函数 Mutation: { // ... }, }
同样的,对于mutation
(变更)操作,我们也是先把schema
完成:
# 定义Mutation根入口 type Mutation { editShareInfo(shareInfo: ShareInput!): Share! } input ShareInput { id: ID! title: String! desc: String! where: String }
然后,补全resolver
函数:
import { updateShareInfo, loadShareById } from './datasource' const resolvers = { Query: { // ... }, Mutation: { editShareInfo: (parent, { shareInfo }, context, info) => { // 更新分享详情,then获取更新后的分享详情 return updateShareInfo(shareInfo.id, shareInfo) .then(loadShareById(shareInfo.id)) }, }, } export default resolvers
到此,我们就实现了这个简单的GraphQL
的Server了。
结语
GraphQL
还有很多内容可以探索,使用。比如,如果用Schema构建函数来生成对象类型,可以标记某一个字段为废弃,并给出废弃原因。这样,在版本迭代时,就可以友好的提示到旧版本的使用者,促使其升级到最新的接口,通过某些检测手段,我们也能很轻松的知道旧版本的使用频率,从而方便的让我们在某一个时间彻底删掉这个字段。
如果说GraphQL
有什么缺点,那可能就是上手确实没那么容易,而且对于后端同学来说,还是有很多坑要踩的,比如缓存,性能问题等。好在目前的GraphQL
的资料已经不像几年前那样的匮乏,不管是官方还是社区,GraphQL
可以参考的资源和解决方案都越来越多了。
不管怎样,单纯的对于前端er来说,如果说上一次前端的技术变革是SPA的普及的话,相信当下一次变革到来时,一定有GraphQL
的影子。
一些链接
- 知乎上很火的GraphQL 为何没有火起来?
- graphpack 一个0配置的
GraphQL server
工具,非常适合初学者去了解GraphQL
,并提供了Codepen的方式在线编辑代码,本篇中的示例就是在这个工具的基础上实现的