GraphQL学习

67 阅读3分钟

什么是 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 ...
  }
}

解析函数接受四个参数,分别为

  1. parent:当前上一个解析函数的返回值
  2. args:查询中传入的参数
  3. context:提供给所有解析器的上下文信息
  4. 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))
        },  
    },
}