很多情况下,你可能不想让 API 返回一个数字或字符串。你可能会期望它返回一个带有复杂行为的对象。GraphQL 刚好可以完美地契合你的这个要求。
在 GraphQL schema language 中,定义一个新的对象类型就和我们在示例中定义的 Query
类型一样。每个对象可以有返回指定类型的字段,以及带有参数的方法。例如,在 参数传递 一节中,我们有一个掷骰子的方法:
type Query {rollDice(numDice: Int!, numSides: Int): [Int]}
如果随着时间的推移我们想要有越来越多的基于随机骰子的方法,我们可以实现一个 RandomDie
的对象类型来替代。
type RandomDie {roll(numRolls: Int!): [Int]}type Query {getDie(numSides: Int): RandomDie}
对于 RandomDie
类型的根级别解析器来说,我们可以用 ES6 的 class 语法来替代,这样的话这些解析器就是这个类的实例方法了。下面的代码展示了如何使用 ES6 的 class 语法来实现上面的 RandomDie
对象类型:
class RandomDie {constructor(numSides) {this.numSides = numSides;}rollOnce() {return 1 + Math.floor(Math.random() * this.numSides);}roll({numRolls}) {var output = [];for (var i = 0; i < numRolls; i++) {output.push(this.rollOnce());}return output;}}var root = {getDie: ({numSides}) => {return new RandomDie(numSides || 6);}}
对于那些不使用任何参数的字段来说,你可以使用对象属性或实例方法来表示。对于上面的示例方法,不论是 numSides
还是 rollOnce
实际上都可以被用来实现 GraphQL 的字段,所以上面的代码同样可以以 schema 的方式来实现:
type RandomDie {numSides: Int!rollOnce: Int!roll(numRolls: Int!): [Int]}type Query {getDie(numSides: Int): RandomDie}
最后把这些代码都整理到一起,这里是一些使用该 GraphQL API 运行服务器的示例代码:
var express = require('express');var { graphqlHTTP } = require('express-graphql');var { buildSchema } = require('graphql');// 用 GraphQL schema language 构造一个 schemavar schema = buildSchema(`type RandomDie {numSides: Int!rollOnce: Int!roll(numRolls: Int!): [Int]}type Query {getDie(numSides: Int): RandomDie}`);// 该类继承 RandomDie GraphQL 类型class RandomDie {constructor(numSides) {this.numSides = numSides;}rollOnce() {return 1 + Math.floor(Math.random() * this.numSides);}roll({numRolls}) {var output = [];for (var i = 0; i < numRolls; i++) {output.push(this.rollOnce());}return output;}}// root 规定了顶层的 API 入口端点var root = {getDie: ({numSides}) => {return new RandomDie(numSides || 6);}}var app = express();app.use('/graphql', graphqlHTTP({schema: schema,rootValue: root,graphiql: true,}));app.listen(4000);console.log('Running a GraphQL API server at localhost:4000/graphql');
当你对一个返回对象类型的 API 发出 GraphQL 查询时,你可以通过嵌套 GraphQL 字段名来一次性调用对象上的多个方法。例如,如果你想在调用 rollOnce
方法掷 1 次骰子的同时也调用 roll
方法来掷 3 次骰子的话,你可以这么做:
{getDie(numSides: 6) {rollOnceroll(numRolls: 3)}}
如果你用 node server.js
命令来运行这些代码并且访问 http://localhost:4000/graphql 的话,你可以用 GraphiQL 试一下这些 API。
这种定义对象类型的方式通常会比传统的 REST 风格的 API 会带来更多的好处。你可以只用一次请求就能获取到所有信息,而不是一次请求只能获取到一个对象的相关信息,然后还要请求一系列 API 才能获取到其他对象的信息。这样不仅节省了带宽、让你的应用跑得更快,同时也简化了你客户端应用的逻辑。
到目前为止,我们所看到的每个 API 都是为返回数据而设计的。为了修改存储的数据或处理复杂的输入,需要继续 学习 mutations 和 input types。