GrqphQL 使用指南(服务器)

在之前的几篇教程中,我们讲的是如何查询和Mutation操作,这些都是在客户端那边所进行的,那么服务器这边是如何处理这些请求的呢?这就是这篇教程所要说的东西了.

准备工作

  1. 克隆库:
  1. git clone https://github.com/zhouyuexie/learn-graphql
  1. 安装依赖:
  1. cd learn-graphql && npm install
  2. cd learn-graphql && git checkout server && npm install
  1. 运行:
  1. npm start

现在打开你的浏览器输入http://localhost:12580/graphql,或者点击这里.

以下教程的代码都在schema.js文件中.

类型

GraphQL中有对应JavaScript的类型:

  1. GraphQLObjectType,//自定义类型
  2. GraphQLSchema,//定义视图
  3. GraphQLInterfaceType,//描述多个类型的通用字段
  4. GraphQLList,//其他类型的封装
  5. GraphQLString,//字符串类型
  6. GraphQLInt,//整型
  7. GraphQLFloat,//浮点型
  8. GraphQLEnumType,//可迭代类型
  9. GraphQLNonNull,//不允许为空类型,接受一个graphql类型

还有一些比如GraphQLInputType等等这类不常用的类型,限于篇幅,我们这里就不做过多的介绍,有兴趣的读者可以去Google一下.

查询热身

打开http://localhost:12580/graphql后在左边输入:

  1. {
  2. receivedMessage: echo(message: "wowo")
  3. }

然后你会得到服务器的返回:

  1. {
  2. "data": {
  3. "receivedMessage": "Hello wowo"
  4. }
  5. }

这是一个非常简单的查询,我们可以看下面的实现代码:

  1. // 查询根目录(关于查询的动作都需要在这里声明)
  2. const Query = new GraphQLObjectType({
  3. name: 'BlogSchema',//根查询名称
  4. description: 'Root of the Blog Schema',
  5. fields: () => ({
  6. // 回应查询
  7. echo: {
  8. type: GraphQLString,
  9. description: '回应你输入的内容',
  10. // 参数定义
  11. args: {
  12. message: {type: GraphQLString}
  13. },
  14. resolve: function(source, {message}) {
  15. return `hello: ${message}`;
  16. }
  17. }
  18. })
  19. });

上面的代码在根查询里定义了一个名为echo的查询字段.

  1. type:值为 GraphQLString 说明了它返回的数据是字符串类型.
  2. args:参数列表,有一个message参数,类型为字符串.
  3. resolve:返回数据.

提示:resovle接受三个参数,第一个参数是当前字段的根字段得到的结果值,而这里已经是根查询了,所以值为undefined,而第二个参数是参数对象,第三个是一个request对象.

测试1

现在请你自己修改一下这个查询,增加一个参数name,然后重启服务器,请求服务器:

  1. {
  2. receivedMessage: echo(message: "hello",name:"wowo")
  3. }

让服务器返回如下数据:

  1. {
  2. "data": {
  3. "receivedMessage": "wowo said hello"
  4. }
  5. }

查询

有人说上面那个根本不算是查询,这个确实,但是入门一个教程没有人一开始就打击你对吧,下面就是一个更高级点的查询,更加接近 “现实生活” .

有这样一个需求:我们需要根据文章的ID号查询文章,服务器返回标题和正文等信息.

开始前我们需要分析一个文章包含什么信息,有什么字段,需要定义出来,如下:

  1. const Post = new GraphQLObjectType({
  2. name:"Post",
  3. description:"一篇文章",
  4. fields:()=>({
  5. _id:{
  6. type:new GraphQLNonNull(GraphQLString),//不允许为空
  7. },
  8. title:{
  9. type:new GraphQLNonNull(GraphQLString),//不允许为空
  10. },
  11. category:{
  12. type:GraphQLString
  13. },
  14. layout:{
  15. type:GraphQLString
  16. },
  17. content:{
  18. type:GraphQLString
  19. },
  20. })
  21. });

一篇文章包含了id,title,category,layout,content这些信息,其中id和title是不允许空的字符串,如果查询到的数据没有这两个就会报错.

定义好后我们就需要在根查询里面建立一个引用,否则定义的就没法使用:

  1. // 查询根目录(关于查询的动作都需要在这里声明)
  2. const Query = new GraphQLObjectType({
  3. name: 'BlogSchema',
  4. description: 'Root of the Blog Schema',
  5. fields: () => ({
  6. // 回应查询
  7. echo: {
  8. // ...
  9. },
  10. // 文章查询
  11. posts:{
  12. type:new GraphQLList(Post),
  13. args:{
  14. index:{type:GraphQLInt}
  15. },
  16. resolve:(source,args)=>{
  17. return [PostsList[args.index]],//返回数组(虽然只有一个)
  18. }
  19. }
  20. });
  21. })

文章查询posts接收一个index参数,这个参数是一个整型.

resolve返回存储在PostsList数组里的对应文章信息,因为posts返回的是一个数组(数组里的数据全是Post对象),而我们查询到的数据是一个Post对象,所以需要用一个数组号括起来.

有时候你需要嵌套几个GraphQLObjectType来得到自己想要的数据格式,比如项目中的schema.js定义了一个地址查询,定义了三层查询.

Mutation

客户端查询数据的时候有时候是也伴随着修改数据和创建数据,所以这里也要介绍一下如果操作更新数据.

我们来看看一个Mutation操作:

  1. mutation CREATE{
  2. createAddress(Id:1,Code:"13156",Name:"信息价",FirstStr:"S"){
  3. Id,
  4. Name,
  5. Code,
  6. }
  7. }

增加一个地级市的信息,这个地级市有以下字段:Id,Code,Name,FirstStr.

CREATE是一个mutation名,并不是关键字,你可以随便取其他名字.

createAddress是服务器定义好的一个关键字,接收四个字段,大括号里返回的是创建好的信息.

我们再来看看服务器这边:

  1. // 操作根目录(关于操作的动作都需要在这里声明)
  2. const Mutation = new GraphQLObjectType({
  3. name:"Mutation",
  4. description:"增删改数据",
  5. fields:()=>({
  6. createAddress:{
  7. type:AddressContent,
  8. args:{
  9. Id:{
  10. type:new GraphQLNonNull(GraphQLInt)
  11. },
  12. Code:{
  13. type:new GraphQLNonNull(GraphQLString)
  14. },
  15. Name:{
  16. type:new GraphQLNonNull(GraphQLString)
  17. },
  18. FirstStr:{
  19. type:new GraphQLNonNull(GraphQLString)
  20. }
  21. },
  22. resolve:(source,args)=>{
  23. let address = Object.assign({},args);//获取数据
  24. //改为大写
  25. address.FirstStr = address.FirstStr.toUpperCase();
  26. let queryData = _.find(AddressList,item=>item.ShortKey===address.FirstStr);//查找的数据
  27. //检测是否存在FirstStr开头的
  28. if(queryData){
  29. // 有这个数据
  30. //存储数据
  31. queryData.Content.push(address);
  32. // console.log(address)
  33. return address;//返回新存储的数据
  34. }
  35. else{
  36. return null;
  37. }
  38. }
  39. }
  40. })
  41. })

数据库

数据库不打算专门写一篇了,其实也就是将查询和修改数据库的操作放在resolve函数里操作就行了.