GraphQL 入门

为什么要使用GraphQL

GraphQL是由Facebook团队在2015年开源推出的一套用于替代传统的REST API的框架。利用它能够大幅提升开发效率以及应用性能。设想这样一个场景:

你想要获得一个用户的朋友的国家信息,如果你使用的是REST API的话,你可能需要同时调用以下这些接口:

  • /users/{userName}/friends: 返回类似 {id: 1, name: 'xxx', friends: [2,3,4] }的结构
  • /friends/{ID}: 返回类似{id: 2, name: 'xxx', country: 23}的结构
  • /countries/{ID}: 返回类似{id: 23, name: 'China', flag: 'xxx', language: 'Chinese'}的信息

先取回以scq000为用户名的朋友id,然后利用这些返回的用户id再查询详细的信息,最后再拼装成想要的数据结构。你会发现,随着应用复杂度的提升,API的数量以及请求的数量成倍地增加,客户端为了想要获得一个简单的数据信息,要往返多次地调用不同的API。

而GraphQL的出现就是为了解决上述这些问题的,它本质上是一门面向API的查询语言(Query Language for API)。可以根据客户端想要的数据,一次性地将数据取回来。

比如,上面的这个例子就能用下面的Graph 请求来描述:

{
    user {
        friend {
            name
            country
        }
    }
}

然后返回的数据是像这样:

{
    "user": {
        "friend": {
            "name": "scq000",
            "country": "China"
        }
    }
}

同时目前GraphQL也支持各种语言,对于主流的编程语言都有良好地支持,生态环境也搭建地不错。国外的大公司,如facebook, paypal, twitter, github等现在都在项目中大量使用GraphQL。

核心概念

GraphQL中一个核心的概念就是Schema, 它是客户端和服务端之间进行沟通的协议,定义好对应的数据schema后,前后端就可以独立地进行开发,从而提高效率。

一个schema是由query和mutation两部分组成的,如:

schema {
    query: Query,
    mutation: Mutation
}

其中, query对应的就是请求数据的对象,而mutation主要负责进行副作用,如create, delete, update等操作。

types

一个GraphQL schema中最基本的组件是对象类型,可以用来表示能从服务器获取什么类型的对象。GraphQL内置的标量数据类型(Scalar Types)有以下几种:

  • Int: a signed 32bit 带符号的32位整数

  • Float: 有符号双精度浮点型

  • String: UTF-8编码的字符串

  • Boolean: 布尔型,true和false两个值

  • ID: 唯一标识符,用以重新获取对象或作为缓存的键

还有枚举类型enum:

enum Country {
  CHINA
  JAPAN
  AMERICAN  
}

默认情况下每一个内置类型的都可以被设置成null,如果需要确保某个字段不为空,需要使用!符号:

type Author {
    id: ID!
    firstName: string,
    courses: [String]
}

注意到courses的类型是[String],这表示这个字段是个数组类型。

除了使用内置类型外,还可以自定义类型:

type Query {
    author_details: [Author]
}

type Mutation {
    addAuthor(firstName: String, lastName: sring): Author
}

Queries

Fields

字段是Query对象上最基本的组成单位,服务端和客户端的结构基本相同:

{
    viewer {
        name
    }
}

Arguments

在GrahQL中,你可以给字段传递参数。这样可以避免重复的API请求,如下所示:

{
    followers (last: 3) {
        nodes {
            id
        }
    }
}

这表示请求最后3个记录,你还可以使用:

{
    author (id: "1000") {
        name
        age
    }
}

这表示请求id为1000的记录。

alias

由于在请求中不能使用不同的参数来请求相同的字段,因此就需要使用alias来给字段起别名:

{
    firstFollowers: followers (first: 3) {
        nodes {
            id
            name
        }
        
    }
    lastFollowers: followers (last: 3) {
        nodes {
            id
            name
        }
    }
}

Fragments

片段(Fragment)是可复用的单位,可以让你在多个Query对象中利用同一个Fragments:

{
    nodes {
        ...userInfo
    }
}

fragment userInfo on User {
    id
    bio
}

fragment关键字声明一个片段对象,然后再使用的时候直接利用扩展运算符...就可以了。

Operation name

我们一般在声明查询对象的时候都是用简写形式省略query关键字和查询名称,不过如果需要减少代码歧义,则需要显式声明:

query ViewerInfo {
    viewer {
        name
        date
    }
}

Variables

字段的参数可以是动态的,所以使用变量进行控制:

query ViewerInfo($isOwner: Boolean!) {
    viewer {
        id
        name
        start(ownedBy)
    }
}
{
    isOwner: false
}

Mutations

用来更新数据的(create, update, delete)的副作用。

Query的时候所有的查询是同时执行的,但是Mutation操作是按顺序执行的:

mutation NewStatus($input: ChangeUserStatusINput!) {
    changeUserStatus(input: $input) {
        clientMutationId
        status {
            message
        }
    }
}

生态以及工具

整个GraphQL架构是有客户端和服务端两部分组成中,其中客户端主要负责处理数据请求的状态管理,缓存,分页,错误处理以及schema的校验等工作。而服务端主要和数据库打交道, 有schema 和resolvers, resolver主要处理业务逻辑。

现在比较主流的GraphQL客户端框架有Apollo ClientRelay

服务端可以使用Apollo Server, express graphql, graphql yoda等。

数据库方面可以使用Prisma, 是替代传统的ORM框架的方案,支持多种数据库。

最后再推荐一些其他有用的工具:

https://graphql.org/swapi-graphql/

graphql voyager: 图示,模型设计的工具

graphql faker: mock 数据

graphql visual editor: 可视化编辑器

如何使用

最后,我们来写一个实际的demo来应用一下GraphQL:

首先我们要先来定义一下schema对象:

import {
    GraphQLSchema,
    GraphQLObjectType,
    GraphQLInt
} from "graphql";

let counter = 1;

let schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'Query',
        fields: () => ({
            counter: {
                type: GraphQLInt,
                resolve: () => counter
            }
        })
    }),
    mutation: new GraphQLObjectType({
        name: "Mutation",
        fields: () => ({
            incrementCounter: {
                type: GraphQLInt,
                resolve: () => ++counter
            }
        })
    })
})

这里定义了一个counter的字段,然后mutation里有一个incrementCounter的方法,每次执行这个mutation,都会让counter的值加1。

有了schema后,接着就可以利用express来开启一个叫做/graphql的接口,并使用已经定义好的schema:

const app = express();
const port = 3000

app.use('/graphql', GraphQLHTTP({
    schema
}));

app.listen(port, () => console.log(`Example app listening on port ${port}`))

最后我们就可以直接在浏览器中输入localhost:3000/graphql使用了

参考资料

https://graphql.org/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容