GraphQL 简介

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isnt tied to any specific database or storage engine and is instead backed by your existing code and data.

GraphQL 是一种 API 查询语言,同时也是基于 自定义类型系统 执行查询的一个服务端运行时(runtime) 。GraphQL 本身不绑定任何数据库或存储引擎,而是可以在现有代码和数据上提供支持。

介绍概念后,一般应该紧跟介绍 GraphQL 的特性,跟前辈 Restful API 的比较等等。不过我觉得对初学者来说(包括我),这样的先后顺序并不友好,特别是对 GraphQL 这样全新的技术,以 demo 来快速了解怎么用,有个大概印象会更好。

所以下面紧接 GraphQL 的应用。

GraphQL 使用

所有示例基于 GraphQL 的 node.js 实现(graphql-js),请确保已安装以下依赖:

  • node>=6
  • npm install graphql,GraphQL 的 JS 实现,提供了两个重要能力:创建 type schema 和 对对应的 schema 执行查询。
  • npm install express express-graphql,创建 GraphQL HTTP server。

1. 最简示例

GraphQL 查询本质是客户端发送字符串到服务端,服务端解析后返回 JSON 给客户端。下面我们尝试构建最基本的 查询。

const { graphql, buildSchema } = require(graphql)

// 使用 GraphQL 的 schema 语言构造一个 schema
const schema = buildSchema(`
  type Query {
    hello: String
  }
`)

// root 为每个 API endpoint 提供 resolver 函数
const root = {
  hello() {
    return `Hello world!`
  }
}
// query
const query = `{ hello }`

graphql(schema, query).then(response => {
  console.log(No root:, response)
  // No root: { data: { hello: null } }
})
graphql(schema, query, root).then(response => {
  console.log(With root:, response)
  // With root: { data: { hello: Hello world! } }
})

这个例子中没有引入客服端和服务端的区分,只是展示了怎么创建 Schema ,query 怎么被处理。下面一个例子结合 express 构建一个正常的 GraphQL 服务。

2. 集成 GraphQL 服务端

服务端使用 expressexpress-graphql 搭建 GraphQL 服务。

const express = require(express)
const graphqlHTTP = require(express-graphql)
const { buildSchema } = require(graphql)

// 创建 schema,需要注意到:
// 1. 感叹号 ! 代表 not-null
// 2. rollDice 接受参数
const schema = buildSchema(`
  type Query {
    quoteOfTheDay: String
    random: Float!
    rollThreeDice: [Int]
    rollDice(numDice: Int!, numSides: Int): [Int]
  }
`)

// The root provides a resolver function for each API endpoint
const root = {
  quoteOfTheDay: () => {
    return Math.random() < 0.5 ? Take it easy : Salvation lies within
  },
  random: () => {
    return Math.random()
  },
  rollThreeDice: () => {
    return [1, 2, 3].map(_ => 1 + Math.floor(Math.random() * 6))
  },
  rollDice: ({ numDice, numSides }) => {
    const output = []
    for (let i = 0; i < numDice; i++) {
      output.push(1 + Math.floor(Math.random() * (numSides || 6)))
    }
    return output
  }
}

const 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)

客户端代码:

const fetch = require(node-fetch)

fetch(http://localhost:4000/graphql, {
  method: POST,
  headers: {
    Content-Type: application/json,
    Accept: application/json
  },
  body: JSON.stringify({
    query: `{
      random,
      quoteOfTheDay,
      rollThreeDice,
      rollDice(numDice: 9, numSides: 6)
    }`
  })
}).then(res => {
  return res.json()
}).then(data => {
  console.log(data)
  /**
   *
   { data:
     { random: 0.3099461279459266,
       quoteOfTheDay: Salvation lies within,
       rollThreeDice: [ 3, 1, 2 ],
       rollDice: [ 2, 6, 4, 1, 1, 5, 2, 1, 1 ] } }
   */
})

上面代码中我们展示了怎集成 express-graphql,前端怎么向后台发起查询等等。需要注意到

  1. 基本类型有:String, Int, Float, Boolean, ID 5 种,我们试验了 String|Float|Int 等 3 种。其中 ID 对应 JavaScript 中的 Symbol
  2. 默认情况下,指定基本类型时返回 null 是合法的。但如果在后面加上感叹号则表示不允许为空,比如这里的 Float!
  3. 给基本类型加上方括号则表示为数组,如 [Int]
  4. 此外,查询时可以使用参数,参数用圆括号包裹,如 rollDice(numDice: 3, numSides: 6)。当然,有时候我们可能不希望把参数直接写死,那么可以这样传参:
    body: JSON.stringify({
      query: `query RollDice($dice: Int!, $sides: Int) {
        rollDice(numDice: $dice, numSides: $sides)
      }`,
      variables: { dice: 9, sides: 6 }
    })
© 版权声明
评论 抢沙发

请登录后发表评论