做者:方芳前端
阅读时间大约10~15mingit
咱们常常说到一个术语叫API
或者说接口。在和服务端交互的时候,咱们会说要一个数据接口。在和客户端交互的时候交互的时候,咱们会说要一个jsapi。API
的解释是这样的:API
是 Application Programming Interface
(应用程序编程接口)。顾名思义,它是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工做机制的细节。github
GraphQL
是Facebook
开发的API查询语言,而不是一个数据库。它为咱们提供了一种更有效的设计、建立和使用 API的方法。从根本上说,它是 REST 的替代品。数据库
GraphQL对前端很友好,使前端开发人员轻松,GraphQL也有一系列的前端库(Apollo、Relay或Urql),前端能够用如缓存、实时更新UI等功能。express
借助GraphQL前端开发人员生产力能够提升,产品开发速度加快,不管UI如何变后端不用变。编程
Facebook发布GraphQL三年以来,社区也已经比较完善,也有不少公司在用。json
用游戏列表来举例说明一下GraphQL的使用。后端
首先咱们要准备的是服务端的游戏列表的数据,须要注意的是,这个数据能够来自数据库、第三方、咱们的上游服务端等,GraphQL自身并非一个数据库:api
[
{ id: 1, game_name: "风暴魔域(注册送好礼)", short_name: "moyu", score: 4.2625498 },
{ id: 2, game_name: "侍魂胧月传说", short_name: "shihun", score: 4.3800301 }
]
复制代码
接下来须要介绍几个GraphQL中常见的特性。缓存
GraphQL 有本身的语言类型,用于编写模式。 这是一种可读很高的模式语法,称为规范与描述语言(SDL)。不管使用何种技术,SDL 都是相同的,也就是说你能够将其用于你想要的任何语言或框架。
这种模式语言很是有用,由于它更直观的看出 API 具备哪些类型,可读性很强。
使用graphql的第一步,咱们须要在graphql服务器编写咱们本身的schema。
type Game {
id: ID!
game_name: String!
short_name: String!
score: Int
}
type Query {
games: [Game!]!
game(id: ID!): Game!
}
type Mutation {
createGame(id: ID!, game_name: String!, short_name: String!, score: Int): Game!
updateGame(id: ID!, game_name: String, short_name: String, score: Int): Game!
deleteGame(id: ID!): Game!
}
复制代码
类型是 GraphQL 最重要的特性之一。类型是表示 API 外观的自定义对象。例如,在游戏大厅中,咱们须要有用户、游戏、礼包、评论等类型。
type Game {
id: ID!
game_name: String!
short_name: String!
score: Int
}
复制代码
类型具备字段,这些字段返回特定类型的数据。 例如,咱们在上面建立的 Game 类型,咱们有一些 game_name,short_name 和 score 字段。 类型字段能够是任何类型,并始终返回一种数据类型,如 Int,Float,String,Boolean,ID,对象类型列表或自定义对象类型。
其中!的意思是,这个字段不能为空,因此上面的Game类型中,只有score是能够为空的。
query是GraphQL 中的获取数据的方式。关于 GraphQL 中的查询,最吸引人的地方之一就是你能够得到所需的确切数据,很少很多。这个点我以为真是的很是的棒,由于咱们如今在定义接口的时候有一个消耗在沟通上的点就是多方协调入参和参数返回。
type Query {
games: [Game!]!
game(id: ID!): Game!
}
复制代码
在这里咱们定义了两个查询,一个是查询全部游戏,一个是查询指定id的游戏。
在 GraphQL 中,更改是修改服务器上的数据并获取更新数据的方式, 能够认为是咱们日常用的增删改。
type Mutation {
createGame(id: ID!, game_name: String!, short_name: String!, score: Int): Game!
updateGame(id: ID!, game_name: String, short_name: String, score: Int): Game!
deleteGame(id: ID!): Game!
}
复制代码
因为实时通讯业务的需求,GraphQL如今也提供了subscription的功能。能够实现服务端对客户端的实时数据传递,意味着不管什么时候在服务器中发生数据变化,而且每当调用该事件时,服务器都会将相应的数据发送到客户端。经过订阅,你可让你的应用在不一样的用户之间保持更新。
基本的订阅的schema是这样写的:
subscription {
games {
id
game_name
short_name
score
}
}
复制代码
讲完了graphql的几个经常使用的特性以后,接下来要来描述一下,咱们在服务器端是怎么告知graphql咱们须要的数据在哪里的。
GraphQL社区提供了一个支持http的中间件,express-graphql,借助此能够很简单实现一个支持graphql的服务器,由于本次不主要介绍服务器的搭建,有兴趣能够查阅github。GraphQL的发明最初是为了提高前端的开发效率,这篇文章所讲只描述在前端中的使用,可是graphql是能够在其余语言中使用的,若是你们有兴趣也能够自行查阅。
在服务器端须要有一个resolvers的js文件,做用是用来告知graphql从哪里去取数据,而且描述对数据的处理。
import { games } from "./db";
const resolvers = {
Query: {
game: (parent, { id }, context, info) => {
return games.find(user => game.id == id);
},
games: (parent, args, context, info) => {
return games;
}
},
Mutation: {
createGame: (parent, { id, game_name, short_name, score }, context, info) => {
const newGame = { id, game_name, short_name, score };
games.push(newGame);
return newGame;
},
updateGame: (parent, { id, game_name, short_name, score }, context, info) => {
let newGame = games.find(game => game.id === id);
newGame.game_name = game_name;
newGame.short_name = short_name;
newGame.score = score;
return newGame;
},
deleteGame: (parent, { id }, context, info) => {
const gameIndex = games.findIndex(game => game.id === id);
if (gameIndex === -1) throw new Error("Game not found.");
const deletedGames = games.splice(gameIndex, 1);
return deletedGames[0];
}
}
};
export default resolvers;
复制代码
以上是对应咱们上面的schema的一份完整的resolver文件。
上面讲的都是服务器端的设置与操做,那客户端应该以什么样的规则来与服务端交互。
以请求游戏列表为例:
query {
games {
id
game_name
short_name
score
}
}
复制代码
若是要请求某个游戏:
query {
game(id: 1) {
id
game_name
short_name
score
}
}
复制代码
若是须要建立一个游戏
mutation {
createGame(id: 3, game_name: "王者荣耀", short_name: "yxzj", score: 4.7686622) {
id
game_name
short_name
score
}
}
复制代码
上面咱们说了客户端和服务端分别该怎么作,可是先后端的交互仍是不可避免的要经过http协议来传递数据。那在GraphQL下,咱们是如何操做的?
首先,仍是要保证咱们的GraphQL服务器支持http。
以查询举例,咱们在服务端须要接受到的是一个query信息,以下面所示:
query {
games {
game_name
}
}
复制代码
咱们该怎么把这些数据传递给服务端?
在get方式下:
http://myapi/graphql?query={games{game_name}}
复制代码
查询变量能够做为 JSON 编码的字符串发送到名为 variables 的附加查询参数中。
若是须要传递的参数不少,建议使用post方式传递数据。
当前来讲,和服务端交互,咱们经常使用的是RESTful API。REST(representational state transfer表象性状态转变)架构指的是URI定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操做,用HTTP Status Code传递Server的状态信息。REST的主要原则有几个:网络上的全部事物都被抽象为资源,每一个资源都有一个惟一的资源标识符,同一个资源具备多种表现形式(xml,json等),对资源的各类操做不会改变资源标识符,全部的操做都是无状态的。
好比,仍是说咱们的games接口,对于“游戏”咱们有增删改查四种操做,怎么定义RESTful的接口?
增长一个游戏,uri: n.ssp.qq.com/api/game 接口类型:POST
删除一个游戏,uri: n.ssp.qq.com/api/game 接口类型:DELETE
修改一个游戏,uri: n.ssp.qq.com/api/game 接口类型:PUT
查找一个游戏,uri: n.ssp.qq.com/api/game 接口类型:GET
由上面两个图片的比较,咱们能够发现,restful api是多入口的,每个请求服务端都有特定的返回,若是客户端须要一些新的数据,必定会涉及服务端的修改。graphql的请求是单一入口的,服务端设置好schema以后,能够根据客户端传入的请求信息可预知的返回客户端须要的信息,而不涉及服务端的修改。