1

它是为了解决什么问题而产生的

当我们的程序变得日益庞大、臃肿,各个业务之间的数据、前端界面之间的耦合度就可能会很高,这样不仅维护成本和开发成本会飙升,而且还可能做的更多重复性的工作。由此我们想到可以将通用的一些东西抽取出来。
针对此问题前端领域衍生出了很多优秀的框架,其中当以react和vue应用最广泛,通过组件化的方式实现代码的重用和相互独立。
而graphql则是针对后端的,前端来决定请求的数据格式,后端返回固定的数据模型,就好像前端发送了sql的查询语句,这样不仅可以解决程序中各个不同业务之间的数据依赖,还可以达到数据的重用。
吧啦吧啦结束了,是时候展现真正的技术了。

实现一个基于graphql的服务

schema

schema应该算是整个graphql的核心内容。它用来描述服务器返回的额数据类型以及结构,而在客户端请求的时候要保证请求的内容与服务器要求的格式一致,举个栗子。
当我们的请求体是这个样子的

{

 article(id: "1") {

   title

  }

}

那么返回的数据结构就是

{

  "data": {

    "article": {

      "title": "graphql初探"

    }

  }

}

这里有必要说明一下,GraphQL 的一个查询请求被称为一份 query 文档(query document),即 GraphQL 服务能够解析验证并执行的一串请求字符串。

...?query={article(id:"1"){title}}

我们来生成一个稍微复杂一点的schema

import { GraphQLObjectType, GraphQLSchema, GraphQLList, GraphQLString } from 'graphql'
import { getArticles } from '../model/article'

const articleType = new GraphQLObjectType({
  name: 'Article',
  fields: {
    ctime: { type: GraphQLString },
    title: { type: GraphQLString },
  }
})

export const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      articleList: {
        type: new GraphQLList(articleType),
        resolve: async (_, args) => {
          return await getArticles()
        }
      }
    }
  })
})

这段代码创建了一个schema实例,它返回了一个可能包含了多个对象的数组,在对象定义了title和ctime字段。返回的数据要指定类型,在graphql中定义了多种类型:

  1. GraphQLInt: 整数,对应 JavaScript 的 Number
  2. GraphQLFloat: 浮点数,对应 JavaScript 的 Number
  3. GraphQLString: 字符串,对应 JavaScript 的 String
  4. GraphQLBoolean: 布尔值,对应 JavaScript 的 Boolean
  5. GraphQLID: ID 值,是一个序列化后值唯一的字符串,可以视作对应 ES 2015 新增的 Symbol
  6. GraphQLList: 数组
  7. GraphQLObjectType: 对象
  8. GraphQLNonNull: 强制类型的值不能为 null,并且在请求出错时一定会报错。可以用于必须保证值不能为 null 的字段
  9. GraphQLUnionType: 联合类型用于描述某个字段能够支持的所有返回类型,即可能返回的类型有多重的情况下。
const PetType = new GraphQLUnionType({
  name: 'Pet',
  types: [Type1, Type2],
  resolveType(value) {
    if (value instanceof A) {
      return Type1;
    }
    if (value instanceof B) {
      return Type2
    }
  }
})

由于GraphQL并不是一个面向图数据库的查询语言,而是一个数据抽象层,包括数据格式、数据关联、查询方式定义与实现等等一揽子的东西。所以我们最后要调用一个查询数据库的服务来获取真正的数据,也即是下面这段代码

resolve: async (_, args) => {
  return await getArticles()
}

连接schema

很明显我们需要将schema 和服务器连接起来。

//server.js
import express from 'express'
import { schema } from './schema'
import graphqlHTTP from 'express-graphql'
import bodyParser from 'body-parser

let app = express()
let PORT = 3000

//Parse post content as text
app.use(bodyParser.text({ type: 'application/graphql' }))

app.get('/articles', graphqlHTTP({schema}))

let server = app.listen(PORT, function() {
  let host = server.address().address
  let port = server.address().port

  console.log('GraphQL listening at http://%s:%s', host, port)
})

在这里推荐使用express-graphql它会优先解析我们的body或query里的参数,如果都没有的话,则会解析header里的参数。
可以通过访问这个地址来验证

http://localhost:3000/articles?query={articleList{ctime,title}}

mutation

另外除了查询操作外我们还有常用的update操作,针对这样的操作很明显查询是满足不了得,所以就要涉及到变异(mutation,即修改数据)

let schema = new GraphQLSchema({
  query: ...
  mutation: new GraphQLObjectType({
    name: 'RootMutationType',
    fields: {
      updateCount: {
        type: GraphQLInt,
        description: 'Update the count',
        resolve: function() {
          count += 1
          return count
        }
      }
    }
  })
})

至此我们已经完成了一个graphql的服务,但是在实际的项目中涉及到的肯定是不止这些,据说在react项目中relay和graphql更配哦。
在 relay + graphql 的模型中,每个组件指定自己需要的数据。Relay 调用数据,数据更新时,提供给组件最新的数据,并在客户端做缓存。app 需要更新数据时,在 Action 中创建一个 graphql 变更,这和 Redux 类似。下次会尝试relay+graphql。


wupengyu
1.8k 声望166 粉丝

写作是为了更好的思考