Ahonn's BlogAhonn's Blog

GraphQL 学习笔记

2019年03月30日预计阅读需要 6 分钟

入门

  • 定义 Query 类型的 schema 进行查询
  • 为每个 API 节点提供 resolver 函数
  • 处理 GraphQL 查询
var { graphql, buildSchema } = require('graphql');

// 使用 GraphQL schema language 构建一个 schema
var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// 根节点为每个 API 入口端点提供一个 resolver 函数
var root = {
  hello: () => {
    return 'Hello world!';
  },
};

// 运行 GraphQL query '{ hello }' ,输出响应
graphql(schema, '{ hello }', root).then((response) => {
  console.log(response);
});

创建 GraphQL 服务器

将处理 GraphQL 查询的脚本替换会 express API 服务器

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

// create GraphQL scheme & resovler

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');

客户端发起 GraphQL 请求

在请求的 query 中发送 GraphQL 请求

var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open("POST", "/graphql");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
xhr.onload = function () {
  console.log('data returned:', xhr.response);
}
xhr.send(JSON.stringify({query: "{ hello }"}));

Schema 类型

默认支持的标量类型有 String、Int、Float、Boolean 和 ID。

默认情况下,每个类型都是可以为空的 —— 意味着所有的标量类型都可以返回 null。使用感叹号可以标记一个类型不可为空,如 String! 表示非空字符串。

如果是列表类型,使用方括号将对应类型包起来,如 [Int] 就表示一个整数列表。

传递 schema 参数

可以在 schema 中的字段后定义改字段需要的参数:

type Query {
  rollDice(numDice: Int!, numSides: Int): [Int]
}

然后在服务端的 resolver 函数中获取到这些参数:

var root = {
  rollDice: function ({ numDice, numSides }) {
    // do somethings
  }
};

在 schema 中定义变量

可以使用 $ 语法来定义一条查询中的变量,并将变量作为单独映射来传递。

query RollDice($dice: Int!, $sides: Int) {
  rollDice(numDice: $dice, numSides: $sides)
}

自定义对象类型

在 GraphQL schema language 中,定义一个新的对象类型就和定义 Query 类型一样。每个对象可以有返回指定类型的字段,以及带有参数的方法。

type RandomDie {
  roll(numRolls: Int!): [Int]
}

type Query {
  getDie(numSides: Int): RandomDie
}

对于 resolver 来说,可以通过 ES6 class 来定义类:

class RandomDie {
  constructor(numSides) {
    this.numSides = numSides;
  }

  roll({numRolls}) {
	  // do somethings
  }
}

var root = {
  getDie: function ({numSides}) {
    return new RandomDie(numSides || 6);
  }
}

变更和输入类型

在 GraphQL 中,使用 Mutation 作为入口端点即可插入或者修改已有数据。

// 修改已有数据
type Mutation {
  setMessage(message: String): String
}

// 查询数据
type Query {
  getMessage: String
}

可以通过 input 关键字来定义输入类型:

// 输入参数类型
input MessageInput {
  content: String
  author: String
}

type Message {
  id: ID!
  content: String
  author: String
}

type Query {
  getMessage(id: ID!): Message
}

type Mutation {
  createMessage(input: MessageInput): Message
  updateMessage(id: ID!, input: MessageInput): Message
}

必须在 GraphQL 查询前面使用关键字 mutation 才能调用变更,并将数据作为 JSON 对象以传入输入类型。

var query = `mutation CreateMessage($input: MessageInput) {
  createMessage(input: $input) {
    id
  }
}`;
xhr.send(JSON.stringify({
  query: query,
  variables: {
    input: {
      author: author,
      content: content,
    }
  }
}));

查询使用 query, 即查询与修改的区别是传入 API 中的 query 参数不同,修改需要以 mutation 开头。不同的服务器实现传递格式应该不同

通过对象创建 schema

使用 GraphQLSchema 构造函数创建 Schema 时,定义 Query 和 Mutation 类型时不用单纯的 Schema Language,而是像对象一样创建它们。

type Query {
  user(id: String): User
}

// 使用对象创建
var queryType = new graphql.GraphQLObjectType({
  name: 'Query',
  fields: {
    user: {
      type: userType,
      // `args` 描述了 `user` 查询接受的参数
      args: {
        id: { type: graphql.GraphQLString }
      },
      // resolver 
      resolve: function (_, {id}) {
        return fakeDatabase[id];
      }
    }
  }
});

不同的是,使用对象创建 scheme 时会将 resolver 函数一起包括在对象中,而不是分开写。