GraphQL 快速入门
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 isn't 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 服务端
服务端使用 express
和 express-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
,前端怎么向后台发起查询等等。需要注意到
- 基本类型有:
String, Int, Float, Boolean, ID
5 种,我们试验了String|Float|Int
等 3 种。其中ID
对应 JavaScript 中的Symbol
。 - 默认情况下,指定基本类型时返回
null
是合法的。但如果在后面加上感叹号则表示不允许为空,比如这里的Float!
。 - 给基本类型加上方括号则表示为数组,如
[Int]
。 - 此外,查询时可以使用参数,参数用圆括号包裹,如
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 } })
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 学习内存 缓存和垃圾回收相关知识
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
学习了
GraphQL 快速入门 在上面一段差不多可以结束了,一个对 GraphQL 没有概念的同学读后可以有基本的印象,也可以稍微写一些相关代码——即快速上手的目的达成。
但平心而论,希望扎实掌握还是需要补一补基础,在公司内部某个应用实践 GraphQL 之后,加上读了一些资料,下面还是补冲更多的基础知识。
用心学 GraphQL 之 Query
在 GraphQL 官网我们可以看到大大的标题:A query language for your API。GraphQL 首先是一个用于 API 的查询语言,同时也是服务端用于解析查询,返回数据的运行时。我们首先从查询语言这个角度入手。
Basic Query Syntax
一个最基本的查询:
把这个查询从浏览器发给后端,那么后端 GraphQL 服务会返回:
是不是非常简单直观(我们暂时不考虑后端实现)?
稍微加一点难度,假设后端有这样的数据:
我们希望查询
id: 1
的用户的姓名,怎么来(怎么运用参数查询)?是不是还是很简单?可能不是,这里引入了一些新概念,下面一一解释:
query
是什么?是 operation,在 GraphQL 中有query|mutation
两种操作。顾名思义,query
是查询,mutation
是插入/更新/删除操作。FetchUser
是 query 的名字,可任意取。好的名字可以提示其它开发这这个 query 是做什么的。user
是 field (字段),而name
是 sub-field (子字段)。id: 1
是参数(argument on the user field)。参数是无所谓顺序的,(id: 1, name: "Mike")
和(name: "Mike", id: 1)
对 GraphQL 服务器来说是一样的。以上所有称为一个 document (文档)。
当一个 query 没有 参数 或
directives
或 名字,那么关键字query
可以省略,如我们开头所示。Querying with Field Aliases & Fragments
现在我们更深入一点 Query。
字段别名(Field Aliases)
接着上面的例子:
假如我们想一次查询获取多个用户呢?现在
data
已经有个user
属性了,多个用户怎么办?所以我们引入 别名:你可以给任意 field 一个有效的 name,然后数据就会匹配到这个 name 上。
可以看到,原来的
user
被换成了各自的别名。同时,这里提一下,query 中每个字段我们都可以用逗号来分隔,不过这是可选的,看个人爱好:
片段(Fragments)
假设产品经理突然希望每个用户的 email 也要获取,我们可能要这样做:
可以看到,我们在重复自己。假设我们又要添加属性呢?我们需要
fragment
来解决这个问题:...
是 spread 操作符,跟 ES6 语法类似。on User
表明UserFragment
只能运用在User
类型上。Inline Fragment
Inline Fragment
用于根据运行时的类型有条件地得到类型。Movie
类型,所以会得到sequel
数据;Tvshow
类型,所以会得到episode
数据。Querying with Directives
如果我们需要一种方法去动态更改 query 的结构和形状,比如 include/skip 某个字段,那么指令(Directives)就是我们所需要的。
现在 GraphQL 提供
@include
和@skip
两种指令(之后可能会更多)。@include(if: Boolean)
仅仅在参数为true
时包括这个字段。@skip(if: Boolean)
仅仅在参数为true
时剔除这个字段。如果两个指令在某个字段上都出现,那么只有在
(skip: false) && (include: true)
时才包括这个字段。