什么是 GraphQL
GraphQL 是一种面向数据的 API 查询风格。
- 和SQL一样,GraphQL是一们查询语言;
- 同样和SQL一样,GraphQL也是一套规范,就像MySQL是SQL的一套实现一样,Apollo,Relay...也是GraphQL规范的实现;
- 与SQL不同的是,SQL的数据源是数据库,而GraphQL的数据源可以是各种各样的Reft API,可以是各种服务、微服务,甚至可以是数据库
详细介绍:graphql.cn/
GraphQL与restful对比
- restful一个接口只能返回一个资源,GraphQL一次可以获取多个资源
- restful用不同的url来区分资源,GraphQL用类型区分资源
操作类型
- query查询:获取数据,比如查找
- mutation变更:对数据进行变更,比如增加、删除、修改
- substription订阅:当数据发送更改,进行消息推送
注意:在 Query 查询字段时,是并行执行的,而在 Mutation 变更的时候,是线性执行,一个接着一个,防止同时变更带来的竞态问题,比如说我们在一个请求中发送了两个 Mutation,那么前一个将始终在后一个之前执行。
schema
schema就是协议,规范,或者可以当他是接口文档
GraphQL规定,每一个schema有一个根(root)query和根(mutation)
请求格式
GraphQL 最常见的是通过 HTTP 来发送请求,那么如何通过 HTTP 来进行 GraphQL 通信呢
举个栗子,如何通过 Get/Post 方式来执行下面的 GraphQL 查询
query {
me {
name
}
}
Get 是将请求内容放在 URL 中,Post 是在 content-type: application/json 情况下,将 JSON 格式的内容放在请求体里
# Get 方式
http://myapi/graphql?query={me{name}}
# Post 方式的请求体
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
返回的格式一般也是 JSON 体
# 正确返回
{
"data": { ... }
}
# 执行时发生错误
{
"errors": [ ... ]
}
如果执行时发生错误,则 errors 数组里有详细的错误信息,比如错误信息、错误位置、抛错现场的调用堆栈等信息,方便进行定位。
实现执行
GraphQL约定,我们需要为Root Query(根查询)和Root Mutation(根变更)里面的每一个字段提供一个resolver的函数。并包装成一个对象暴露出去
const resolvers = {
// 这里面写查询操作字段的resolver函数
Query: {},
// 这里面写变更操作字段的resolver函数
Mutation: {},
}
export default resolvers
解析函数 Resolver
前端请求信息到达后端之后,需要由解析函数 Resolver 来提供数据,比如这样一个 Query:
query {
hello
}
那么同名的解析函数应该是这样的
Query: {
hello (parent, args, context, info) {
return ...
}
}
解析函数接受四个参数,分别为
- parent:当前上一个解析函数的返回值
- args:查询中传入的参数
- context:提供给所有解析器的上下文信息
- info:一个保存与当前查询相关的字段特定信息以及 schema 详细信息的值
例子:
对于mutation操作,我们也是先把schema完成
# 定义Mutation根入口
type Mutation {
editShareInfo(shareInfo: ShareInput!): Share!
}
input ShareInput {
id: ID!
title: String!
desc: String!
where: String
}
然后补全resolver函数
// 一些加载数据的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: {
editShareInfo: (parent, { shareInfo }, context, info) => {
// 更新分享详情,then获取更新后的分享详情
return updateShareInfo(shareInfo.id, shareInfo)
.then(loadShareById(shareInfo.id))
},
},
}