Skip to content

三种基本操作 Query(a read‐only fetch), Mutation(a write followed by a fetch), Subscription(订阅,不常用,适合长连接业务)

1. 起步,用express实现最简单的例子

运行一个GraphQL API Server

GraphiQL is a great tool for debugging and inspecting a server, so we recommend running it whenever your application is in development mode. image.png

index.js

javascript
const express = require('express');
const graphqlHTTP = require('express-graphql');
const schema = require('./schema');
const app = express();
// 具体选项含义见文档
// https://github.com/graphql/express-graphql
app.use('/graphql', graphqlHTTP({
  schema: schema,
  graphiql: true,
}));

app.listen(12580);
console.log("please open http://localhost:12580/graphql")
const express = require('express');
const graphqlHTTP = require('express-graphql');
const schema = require('./schema');
const app = express();
// 具体选项含义见文档
// https://github.com/graphql/express-graphql
app.use('/graphql', graphqlHTTP({
  schema: schema,
  graphiql: true,
}));

app.listen(12580);
console.log("please open http://localhost:12580/graphql")

schema.js

javascript
const {
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} = require('graphql');

const Query = new GraphQLObjectType({
  name: 'BlogSchema',
  description: 'Root of the Blog Schema',
  fields: () => ({
    echo: {
      type: GraphQLString,
      description: '回应你输入的内容',
      args: {
        message: {type: GraphQLString}
      },
      resolve: function(source, {message}) {
        return `hello: ${message}`;
      }
    },
  })
});


const Schema = new GraphQLSchema({
  query: Query,
});

module.exports = Schema;
const {
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} = require('graphql');

const Query = new GraphQLObjectType({
  name: 'BlogSchema',
  description: 'Root of the Blog Schema',
  fields: () => ({
    echo: {
      type: GraphQLString,
      description: '回应你输入的内容',
      args: {
        message: {type: GraphQLString}
      },
      resolve: function(source, {message}) {
        return `hello: ${message}`;
      }
    },
  })
});


const Schema = new GraphQLSchema({
  query: Query,
});

module.exports = Schema;

注意点:

  • 上篇讲过GraphQL只是一套规范,目前官方只提供了JS版本,其他PHP,Go等其他语言都是社区实现的。

  • 当只有一种操作,并且是query,可以省去query操作名关键字 image.png

  • 可以看到字段和字段参数要指定类型,因为GraphQL是强类型的。

  • 因为指定了参数类型是string,输入时必须要用双引号

  • 注意看调试面板的请求

GraphQL API server运行时,只要构造http请求就可以,传入不同的query参数,也能得到和在GraphiQL同样的结果 image.png

2. 查询

2.1 变量

所有声明的变量都必须是标量、枚举型或者输入对象类型。 image.png

默认变量

使用默认值 image.pngimage.png

覆盖默认值 image.png

类型后面带个感叹号表示参数必填 image.png

2.2 别名

image.png

2.3 片段

提取公众的部分 上面的查询,将共同的字段:id和name,提取成fragment image.png

2.4 指令

GraphQL内置两个核心指令,@skip@include@skip(if: Boolean) 如果参数为 true,跳过此字段。 ? 貌似参数必须要写默认值 ? image.png

image.png

3. 修改 Mutation

详见

4. 分页

分页的原理:定义一个Edges类型,包含node和cursor字段,Node保存查询列表内容,Cursor记录分页。以下面的Github例子

5. GitHub GraphQL API

打开 https://developer.github.com/v4/explorer/ 先打开右侧的Docs浏览所有Query,发现有个名为search的query image.png 他返回的是个 SearchResultItemConnection!类型,接着点进去

image.png

你会发现所有已Connection结尾的类型,其结果都包含pageInfo, edges, nodes

输入下面的内容,这个查询是返回包含"graphql"关键字的前三个仓库,并显示每个仓库的前3个issues的作者,头像信息。

javascript
{
  search(first: 3, query: "graphql", type: REPOSITORY) {
    codeCount
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
    edges {
      cursor
      
      node {
        
        ... on Repository {
          nameWithOwner
          issues(first: 3) {
            nodes {
              author {
                avatarUrl
                login
                resourcePath
                url
              }
              title
            }
          }
        }
      }
    }
  }
  rateLimit {
    cost
    limit
    nodeCount
    resetAt
    remaining
  }
}
{
  search(first: 3, query: "graphql", type: REPOSITORY) {
    codeCount
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
    edges {
      cursor
      
      node {
        
        ... on Repository {
          nameWithOwner
          issues(first: 3) {
            nodes {
              author {
                avatarUrl
                login
                resourcePath
                url
              }
              title
            }
          }
        }
      }
    }
  }
  rateLimit {
    cost
    limit
    nodeCount
    resetAt
    remaining
  }
}

返回的结果类似

json
{
  "data": {
    "search": {
      "codeCount": 16287,
      "pageInfo": {
        "startCursor": "Y3Vyc29yOjE=",
        "endCursor": "Y3Vyc29yOjM=",
        "hasNextPage": true,
        "hasPreviousPage": false
      },
      "edges": [
        {
          "cursor": "Y3Vyc29yOjE=",
          "node": {
            "nameWithOwner": "facebook/graphql",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/540892?v=4",
                    "login": "raymondfeng",
                    "resourcePath": "/raymondfeng",
                    "url": "https://github.com/raymondfeng"
                  },
                  "title": "Possibility of collaboration with LoopBack framework?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars1.githubusercontent.com/u/825073?v=4",
                    "login": "luisobo",
                    "resourcePath": "/luisobo",
                    "url": "https://github.com/luisobo"
                  },
                  "title": "Pagination?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars3.githubusercontent.com/u/71047?v=4",
                    "login": "KyleAMathews",
                    "resourcePath": "/KyleAMathews",
                    "url": "https://github.com/KyleAMathews"
                  },
                  "title": "Custom Sorting"
                }
              ]
            }
          }
        },
        {
          "cursor": "Y3Vyc29yOjI=",
          "node": {
            "nameWithOwner": "graphql-go/graphql",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/78585?v=4",
                    "login": "sogko",
                    "resourcePath": "/sogko",
                    "url": "https://github.com/sogko"
                  },
                  "title": "Suggestion: Improve package discovery"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/1064547?v=4",
                    "login": "ptomasroos",
                    "resourcePath": "/ptomasroos",
                    "url": "https://github.com/ptomasroos"
                  },
                  "title": "Why not wrap the C lib?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/1000404?v=4",
                    "login": "chris-ramon",
                    "resourcePath": "/chris-ramon",
                    "url": "https://github.com/chris-ramon"
                  },
                  "title": "Using graphql-go in other programs throws various errors."
                }
              ]
            }
          }
        },
        {
          "cursor": "Y3Vyc29yOjM=",
          "node": {
            "nameWithOwner": "Youshido/GraphQL",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
                    "login": "mrbarletta",
                    "resourcePath": "/mrbarletta",
                    "url": "https://github.com/mrbarletta"
                  },
                  "title": "How to manage a List of Posts"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
                    "login": "mrbarletta",
                    "resourcePath": "/mrbarletta",
                    "url": "https://github.com/mrbarletta"
                  },
                  "title": "No way to get requested fields of the object inside `resolve`"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/971254?v=4",
                    "login": "larswolff",
                    "resourcePath": "/larswolff",
                    "url": "https://github.com/larswolff"
                  },
                  "title": "Minor documentation issue?"
                }
              ]
            }
          }
        }
      ]
    },
    "rateLimit": {
      "cost": 1,
      "limit": 5000,
      "nodeCount": 12,
      "resetAt": "2018-03-31T01:47:34Z",
      "remaining": 4995
    }
  }
}
{
  "data": {
    "search": {
      "codeCount": 16287,
      "pageInfo": {
        "startCursor": "Y3Vyc29yOjE=",
        "endCursor": "Y3Vyc29yOjM=",
        "hasNextPage": true,
        "hasPreviousPage": false
      },
      "edges": [
        {
          "cursor": "Y3Vyc29yOjE=",
          "node": {
            "nameWithOwner": "facebook/graphql",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/540892?v=4",
                    "login": "raymondfeng",
                    "resourcePath": "/raymondfeng",
                    "url": "https://github.com/raymondfeng"
                  },
                  "title": "Possibility of collaboration with LoopBack framework?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars1.githubusercontent.com/u/825073?v=4",
                    "login": "luisobo",
                    "resourcePath": "/luisobo",
                    "url": "https://github.com/luisobo"
                  },
                  "title": "Pagination?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars3.githubusercontent.com/u/71047?v=4",
                    "login": "KyleAMathews",
                    "resourcePath": "/KyleAMathews",
                    "url": "https://github.com/KyleAMathews"
                  },
                  "title": "Custom Sorting"
                }
              ]
            }
          }
        },
        {
          "cursor": "Y3Vyc29yOjI=",
          "node": {
            "nameWithOwner": "graphql-go/graphql",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/78585?v=4",
                    "login": "sogko",
                    "resourcePath": "/sogko",
                    "url": "https://github.com/sogko"
                  },
                  "title": "Suggestion: Improve package discovery"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/1064547?v=4",
                    "login": "ptomasroos",
                    "resourcePath": "/ptomasroos",
                    "url": "https://github.com/ptomasroos"
                  },
                  "title": "Why not wrap the C lib?"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/1000404?v=4",
                    "login": "chris-ramon",
                    "resourcePath": "/chris-ramon",
                    "url": "https://github.com/chris-ramon"
                  },
                  "title": "Using graphql-go in other programs throws various errors."
                }
              ]
            }
          }
        },
        {
          "cursor": "Y3Vyc29yOjM=",
          "node": {
            "nameWithOwner": "Youshido/GraphQL",
            "issues": {
              "nodes": [
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
                    "login": "mrbarletta",
                    "resourcePath": "/mrbarletta",
                    "url": "https://github.com/mrbarletta"
                  },
                  "title": "How to manage a List of Posts"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars2.githubusercontent.com/u/2429244?v=4",
                    "login": "mrbarletta",
                    "resourcePath": "/mrbarletta",
                    "url": "https://github.com/mrbarletta"
                  },
                  "title": "No way to get requested fields of the object inside `resolve`"
                },
                {
                  "author": {
                    "avatarUrl": "https://avatars0.githubusercontent.com/u/971254?v=4",
                    "login": "larswolff",
                    "resourcePath": "/larswolff",
                    "url": "https://github.com/larswolff"
                  },
                  "title": "Minor documentation issue?"
                }
              ]
            }
          }
        }
      ]
    },
    "rateLimit": {
      "cost": 1,
      "limit": 5000,
      "nodeCount": 12,
      "resetAt": "2018-03-31T01:47:34Z",
      "remaining": 4995
    }
  }
}
  • 每个node包含一个cursor游标,不是数字,是唯一字符串
  • 如果想查下一页,直接修改query search,添加after参数。 search(first: 3, after:"Y3Vyc29yOjM=", query: "graphql", type: REPOSITORY) {
  • 关于实现原理,参考

最后欢迎 clone 我的仓库, 里面包含了所有例子。