graphql
1、
只返回所需
Describe your data
type Project {
name: String
tagline: String
contributors: [User]
}
Ask for what you want
{
project(name: "GraphQL") {
tagline
}
}
Get predictable results
{ "project": { "tagline": "A query language for APIs" } }
GraphQL | A query language for your API https://graphql.org/
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
我的前端故事----我为什么用GraphQL - F-happy - 博客园 https://www.cnblogs.com/fuhuixiang/p/7479276.html
我的前端故事----我为什么用GraphQL
背景
今年我在做一个有关商户的app,这是一个包含商户从入网到审核、从驳回提交到入网维护的完整的生命周期线下推广人员使用的客户端软件,但故事并没有这么简单。。。
疑问
随着app的逐渐完善,遇到的问题也渐渐多了起来,界面加载过久,初始化页面请求次数过多等各种各样的小毛病开始凸显了出来。于是我开始了优化之路,第一步便是从api请求入手,仔细查看了每个api返回的内容,一直奇怪为什么接口总是返回很多的数据回来,比如我需要一个商户的详细信息,可接口却会把这个商户相关的门店信息、所有人信息等其它各种各样的信息一起返回回来,如果是例如商户详情页面也就罢了,可是在商户列表这个接口下依旧返回如此之多的数据,可想而知这个列表有多大多复杂了。
后来问过后端的同学才知道是为了兼容 web 端的需求,一个接口需要同时为多个平台提供内容,这大大增加了接口的返回内容和处理逻辑,而且需求也经常改动,所以还不如把能用到的字段全都输出出来,免得每次改需求都要前后端一起联动。
反思
得知了结果,这确实是一个有“充分”理由的处理结果,可是真的只能这样了嘛?有没有什么更好的解决办法呢?我们先来总结一下现在遇到的问题:
- 兼容多平台导致字段冗余
- 一个页面需要多次调用 API 聚合数据
- 需求经常改动导致接口很难为单一接口精简逻辑
以上三个问题看起来并不复杂,按照以往的逻辑其实也是很好解决的,就拿第一个来说,遇到多平台需要兼容时其实可以通过提供不同平台的接口来解决,例如这样:
http://api.xxx.com/web/getUserInfo/:uid
http://api.xxx.com/app/getUserInfo/:uid
http://api.xxx.com/mobile/getUserInfo/:uid
又或者是通过不同的参数去控制:
http://api.xxx.com/getUserInfo/:uid?platfrom=web
虽然这是一个方便的解决方案,但带来的其实是后端逻辑的增加,需要为不同平台维护不同的逻辑代码。
再说第二个问题,一个页面需要多次调用接口来聚合数据这块也可以通过多加接口的方式来解决:
http://api.xxx.com/getIndexInfo
或者是通过 http2 来复用请求,但这些方法不是增加工作量就是有兼容性问题,那么还有没有其他的方法呢?
如果大家还记得数据库的知识的话,就发现其实我们可以用 SQL 的思路去解决这些事情,那如果把后端抽象成一个数据库会怎么样呢?
我想要什么字段就 SELECT 什么字段就行咯,如果一个页面需要多个数据源的内容来填充那不就是组合 SQL 语句嘛。这样不就解决了上面提出的三个问题了嘛,而且无论前端需求如何变更,只要我们维持一个数据的超集,那么每次只要让前端改动查询语句就可以了,后端这里也不需要同步的去给某个接口增加字段什么的了,那么解决方案有了,那该怎么把后端抽象成一个数据库呢?
解决
既然思路有了,那么办法也会有的~这就是 Facebook 在2015年开源的 GraphQL
这又是一个什么东西呢?具体的介绍直接看它的官网就好了,我在这里就不多说了,直接来看看如何使用吧。
由于我的中间层是基于 Koa2 的,所以就在 koa2 上面做演示了,手写我们先安装依赖:
npm install graphql koa-graphql --save
这样我们就可以在 koa 中使用 graphql 了,然后就是配置路由了,按照文档上面的例子,我们可以这样写:
"use strict";
const router = require('koa-router')();
const graphqlHTTP = require('koa-graphql');
const GraphQLSchema = require('./graphql');
const renderGraphiQL = require('../utils/render_graphiQL');
const graphqlModule = graphqlHTTP((request) => ({
schema: GraphQLSchema,
graphiql: false,
context: {token: request.header.authorization, platform: request.query.platform},
formatError: error => ({
type: 'graphql',
path: error.path,
message: error.message,
locations: error.locations ? error.locations[0] : null
})
}));
router.all('/graphql', graphqlModule);
我们来看看 graphqlModule 对象中都包含写什么吧,首先是 schema,这个是我们主要的解析逻辑,所有通过 graphql 的请求都会被传入这里进行解析和处理,graphiql 这个是 koa-graphql 自带的一个图形界面版的测试地址,后面我再单独介绍这个插件,context 是我们的上下文,如果我们需要在每个解析函数内获取到例如用户 token时就可以在这里赋值,需求说明的是这个必须是一个 object 对象,并且如果我们不指定的话会默认传整个 request 对象,接下来就是最后一个常用的属性了,formatError 是格式化错误的属性,我们可以根据业务的需求自定义我们的错误返回。
接下来去看看从最简单的 hello world 开始,然后完成一个最基础的 demo。
首先我们在客户端发起一个 post 请求,然后在请求 body 中带上我们的查询语句,在 Graphql 中有两种类型的查询,一直是 query 开头的查询操作,一种是 mutation 开头的修改操作。
query {hello}
这是一个最简单的查询,那么这个查询是如何通过解析的呢?上文说到全部的 Graphql 查询都会通过 schema 来进行解析,那我们看看上面定义的GraphQLSchema对象是个什么吧。
module.exports = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'rootQueryType',
description: '查询操作',
fields: {
hello: {
type: GraphQLString,
description: '演示 demo',
args: {
name: {type: GraphQLString, description: '演示参数'}
},
resolve(it, args, context) {
return args.name;
}
}
}
}),
mutation: new GraphQLObjectType({
name: 'rootMutationType',
description: '新增或修改操作',
fields: {}
})
});
我们一步步来说,首先在这个文件中导出的是一个 GraphQLSchema 对象,这是 Graphql 的基础对象,里面包含了我们需要的两种类型,然后看 query 属性,它返回的是一个GraphQLObjectType对象,这是 Graphql 中对于 object 的基本类型,这个对象中包含 name:名称(全局唯一),description: 描述(这会自动的显示在文档中,虽然是非必须的,但是我还是强烈建议每个 Graphql 节点都写上,这样在后面的维护和查询中都非常有利),最后就是fields属性了,一个 Graphql 语句能查到什么就全靠这里写了什么了,在开始的例句中我们查询 query{hello},其实就是说我们要查根节点下的 hello 属性,所以这里我们就需要在 query 的 fields 中写上 hello 属性了,否则这条查询语句就无法生效了。
接下来我们看看这个 hello 属性中又包含了什么呢?首先我们需要指定它的类型,这很关键,这个类型是 hello 的返回类型,在这里我指定它返回的是一个字符串,除此之外还有 Int,Boolean 等 js 的基础类型可供选择,具体可去查看文档,当然了,在复杂情况下也可以返回 GraphQLObjectType 这种对象类型,然后就是对 hello 的描述字段description,接下来是args 属性,如果我们需要给这次查询传入参数的话就靠这个了,接下来就是最关键的 resolve 函数了,这个函数接受三个参数,第一个是上层的返回值,这在循环嵌套的情况下会经常使用,比如说如果 hello 还有子属性的话,那么子属性的这个参数就会是 args.name,第二个参数便是查询属性,第三个是我们一开始说的贯穿整个请求的上下文。下面是一个完整的例子:
Request: query{hello(name: "world")}
Response: {"hello": "world"}
讲完了 query 操作,其实 mutation 操作也是类似的,我就不再展开说了。
总结
最后来总结一下这个解决方案吧,其实这个方案你说是不是最佳解呢?也未必,还是要看具体的业务场景的,在我遇到的场景中各种数据的关系是明确的,或者说是可以抽象成模型的,我认为这是能否使用 Graphql 的关键,从上面的实例中我们其实可以发现通过 Graphql 我们把每个数据都规范了起来,指定了类型,确定了嵌套关系,这在以 JavaScript 为基础的 node 环境中显得那么格格不入,本身 JavaScript 是弱类型的,基于此我们可以在 node 服务中灵活的修改数据,从而不需要关心返回值和参数值,但是 Graphql 用一种强类型的观念来强制我们设计每个数据,也许会有些前端的同学接受不了,但是我个人认为这种思路其实是非常合理的,并且 Graphql 这种还支持嵌套查询,只需要 fields 属性中有这个对象就行了,因此我们可以把每个数据类型尽可能的抽象和分离出来,举个例子,店长这个角色不就是用户对象加上商户对象的组合嘛,这不仅从关系上明确了逻辑,也方便了更多可能性的组合条件。
对应我一开始遇到的那几个问题,Graphql 看上去似乎完美的解决了我的问题,但是对于更加复杂的场景呢?或者说对于老项目的改造成本是否划算呢?虽然我没有遇到,但是我觉得只要认真梳理数据结构,最终都可以的,但那个时候是否还需要 Graphql 呢?那就不知道了,这篇博客不是介绍 Graphql 如何使用的中文文档,我想表达的是这种思路对于这种场景下的一个解决思路,现在它只是解决了我的这些问题,那么从这个思路的身上能不能挖掘出更多的惊喜呢?它还是太新了,也许过几年回头再看,它说不定就和 restful 一样是 API 的标配了也说不定呢,毕竟 GitHub 今年也推出了他们的 Graphql API 了呢。
A query language for your API
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
Ask for what you need,
get exactly that
Send a GraphQL query to your API and get exactly what you need, nothing more and nothing less. GraphQL queries always return predictable results. Apps using GraphQL are fast and stable because they control the data they get, not the server.
{
hero {
name
height
m
}
}
{
"hero": {
"name": "Luke Skywalker",
"height": 1.72,
"mass": 77
}
}
Get many resources
in a single request
GraphQL queries access not just the properties of one resource but also smoothly follow references between them. While typical REST APIs require loading from multiple URLs, GraphQL APIs get all the data your app needs in a single request. Apps using GraphQL can be quick even on slow mobile network connections.
{
hero {
name
friends {
name
}
}
}
{
"hero": {
"name": "Luke Skywalker",
"friends": [
{ "name": "Obi-Wan Kenobi" },
{ "name": "R2-D2" },
{ "name": "Han Solo" },
{ "name": "Leia Organa" }
]
}
}
Describe what’s possible
with a type system
GraphQL APIs are organized in terms of types and fields, not endpoints. Access the full capabilities of your data from a single endpoint. GraphQL uses types to ensure Apps only ask for what’s possible and provide clear and helpful errors. Apps can use types to avoid writing manual parsing code.
{
hero {
name
friends {
name
homeWorld {
name
climate
}
species {
name
lifespan
origin {
name
}
}
}
}
}
type Query {
hero: Character
}
type Character {
name: String
friends: [Character]
homeWorld: Planet
species: Species
}
type Planet {
name: String
climate: String
}
type Species {
name: String
lifespan: Int
origin: Planet
}
Move faster with
powerful developer tools
Know exactly what data you can request from your API without leaving your editor, highlight potential issues before sending a query, and take advantage of improved code intelligence. GraphQL makes it easy to build powerful tools like GraphiQL by leveraging your API’s type system.
Evolve your API
without versions
Add new fields and types to your GraphQL API without impacting existing queries. Aging fields can be deprecated and hidden from tools. By using a single evolving version, GraphQL APIs give apps continuous access to new features and encourage cleaner, more maintainable server code.
type Film {
title: String
episode: Int
releaseDate: String
}
type Film {
title: String
episode: Int
releaseDate: String
openingCrawl: String
}
type Film {
title: String
episode: Int
releaseDate: String
openingCrawl: String
director: String
}
type Film {
title: String
episode: Int
releaseDate: String
openingCrawl: String
director: String
directedBy: Person
}
type Person {
name: String
directed: [Film]
actedIn: [Film]
}
type Film {
title: String
episode: Int
releaseDate: String
openingCrawl: String
director: String @deprecated
directedBy: Person
}
type Person {
name: String
directed: [Film]
actedIn: [Film]
}
Bring your own
data and code
GraphQL creates a uniform API across your entire application without being limited by a specific storage engine. Write GraphQL APIs that leverage your existing data and code with GraphQL engines available in many languages. You provide functions for each field in the type system, and GraphQL calls them with optimal concurrency.
type Character {
name: String
homeWorld: Planet
friends: [Character]
}
// type Character {
class Character {
// name: String
getName() {
return this._name
}
// homeWorld: Planet
getHomeWorld() {
return fetchHomeworld(this._homeworldID)
}
// friends: [Character]
getFriends() {
return this._friendIDs.map(fetchCharacter)
}
}
# type Character {
class Character:
# name: String
def name(self):
return self._name
# homeWorld: Planet
def homeWorld(self):
return fetchHomeworld(self._homeworldID)
# friends: [Character]
def friends(self):
return map(fetchCharacter, self._friendIDs)
// type Character {
public class Character {
// name: String
public String Name { get; }
// homeWorld: Planet
public async Task<Planet> GetHomeWorldAsync() {
return await FetchHomeworldAsync(_HomeworldID);
}
// friends: [Character]
public async IEnumerable<Task<Character>> GetFriendsAsync() {
return _FriendIDs.Select(FetchCharacterAsync);
}
}
Who’s using GraphQL?
Facebook's mobile apps have been powered by GraphQL since 2012. A GraphQL spec was open sourced in 2015 and is now available in many environments and used by teams of all sizes.
一文带你看懂 GraphQL 是什么?- 红帽 https://www.redhat.com/zh/topics/api/what-is-graphql
概述
GraphQL 是一种用于应用编程接口(API)的查询语言和服务器端运行时,它可以使客户端准确地获得所需的数据,没有任何冗余。
GraphQL 旨在让 API 变得快速、灵活并且为开发人员提供便利。它甚至可以部署在名为 GraphiQL 的集成开发环境(IDE)中。作为 REST 的替代方案,GraphQL 允许开发人员构建相应的请求,从而通过单个 API 调用从多个数据源中提取数据。
此外,GraphQL 还可让 API 维护人员灵活地添加或弃用字段,而不会影响现有查询。开发人员可以使用自己喜欢的方法来构建 API,并且 GraphQL 规范将确保它们以可预测的方式在客户端发挥作用。
模式、解析器及其他常见的 GraphQL 术语
API 开发人员使用 GraphQL 创建一个模式,用于描述客户端可通过该服务查询的所有可能数据。
GraphQL 模式由对象类型组成,它表示可以请求哪种对象以及它有哪些字段。
有查询时,GraphQL 会根据模式对查询进行验证。随后,GraphQL 将执行经过验证的查询。
API 开发人员将模式中的每个字段附加到名为解析器的函数中。执行期间,系统将调用解析器来生成相应的值。
除了为 API 查询定义和验证语法外(有关概述见 graphql-spec 存储库),GraphQL 把大部分决策权都留给了 API 设计人员。GraphQL 不提供有关如何存储数据或使用哪种编程语言的任何指导;开发人员可以使用PHP(graphql-php)、Scala(Sangria)、Python(Graphene Python)、Ruby(graphql-ruby)、JavaScript(graphql.js)等等。GraphQL 对网络、授权或分页没有任何要求。
从客户端的角度看,最常见的 GraphQL 操作可能就是查询和修改。如果按照创建、读取、更新和删除(CRUD)模型来审视这些操作,那么查询就等同于读取。其他所有操作(创建、更新和删除)均视为修改。
试用红帽 OpenShift API 管理
获得 60 天的自助服务体验,探索全托管式 API 服务的好处。
GraphQL 在企业环境中的利弊
想在业务或企业环境中试用 GraphQL?这样做既有优点,也有缺点。
优点
- GraphQL 模式会在 GraphQL 应用中设置单一事实来源。它为企业提供了一种整合其整个 API 的方法。
- 一次往返通讯可以处理多个 GraphQL 调用。客户端可得到自己所请求的内容,不会超量。
- 严格定义的数据类型可减少客户端与服务器之间的通信错误。
- GraphQL 具有自检功能。客户端可以请求一个可用数据类型的列表。这非常适合文档的自动生成。
- GraphQL 允许应用 API 进行更新优化,而无需破坏现有查询。
- 许多开源 GraphQL 扩展可提供 REST API 所不具备的功能。
- GraphQL 不指定特定的应用架构。它能够以现有的 REST API 为基础,并与现有的 API 管理工具配合使用。
缺点
- 即便是熟悉 REST API 的开发人员,也需要一定时间才能掌握 GraphQL。
- GraphQL 将数据查询的大部分工作都转移到服务器端,由此增加了服务器开发人员工作的复杂度。
- 根据不同的实施方式,GraphQL 可能需要不同于 REST API 的 API 管理策略,尤其是在考虑速率限制和定价的情况下。
- 缓存机制比 REST 更加复杂。
- API 维护人员还会面临编写可维护 GraphQL 模式的额外任务。
GraphQL 查询示例
想要更好地了解 GraphQL,最好的办法就是看一些查询和响应的示例。我们来看看来自 GraphQL 项目网站 graphql.org 的 3 个示例。
第一个示例显示了客户端如何构建 GraphQL 查询,从而要求 API 以指定的形态返回特定字段。
{ me { name } }
GraphQL API 将以 JSON 格式返回如下结果:
{ "me": { "name": "Dorothy" } }
客户端也可以作为 GraphQL 查询的一部分来传递参数,如下例所示:
{ human(id: "1000") { name location } }
结果为:
{ "data": { "human": { "name": "Dorothy, "location": "Kansas" } } }
从这里开始,事情变得更加有趣。GraphQL 允许用户定义可复用的片段并分配变量。
假设您需要请求一个 ID 列表,然后为每个 ID 请求一系列记录。借助 GraphQL,您可以构建一个查询,然后通过单个 API 调用来提取所需的所有内容。
查询方式如下:
query HeroComparison($first: Int = 3) { leftComparison: hero(location: KANSAS) { ...comparisonFields } rightComparison: hero(location: OZ) { ...comparisonFields } } fragment comparisonFields on Character { name friendsConnection(first: $first) { totalCount edges { node { name } } } }
可能生成以下结果:
{ "data": { "leftComparison": { "name": "Dorothy", "friendsConnection": { "totalCount": 4, "edges": [ { "node": { "name": "Aunt Em" } }, { "node": { "name": "Uncle Henry" } }, { "node": { "name": "Toto" } } ] } }, "rightComparison": { "name": "Wizard", "friendsConnection": { "totalCount": 3, "edges": [ { "node": { "name": "Scarecrow" } }, { "node": { "name": "Tin Man" } }, { "node": { "name": "Lion" } } ] } } } }
如果您是 GitHub 用户,那么使用 GitHub 的 GraphQL Explorer 就能快速上手 GraphQL。
GraphQL 与开源
GraphQL 由 Facebook 开发,并于 2012 年首次应用于移动应用。GraphQL 规范于 2015 年实现开源。现在,它受 GraphQL 基金会监管。
有大量开源项目都涉及 GraphQL。下面的列表虽不详尽,但大致涵盖了一些旨在促进 GraphQL 普及的项目。
- Apollo:一个包含前端客户端库(Apollo 客户端)和后端服务器框架(Apollo 服务器)的 GraphQL 平台。
- Offix:一个离线客户端,即使在无法访问应用的情况下也允许执行 GraphQL 修改与查询。
- Graphback:一个用于生成支持 GraphQL 的 Node.js 服务器的命令行客户端。
- OpenAPI-to-GraphQL:一个用于将 OpenAPI 规范或 Swagger 所描述的 API 转换为 GraphQL 的命令行界面和库。