GraphQL学习笔记(3)入门、查询与变更
一、入门
GraphQL是一种用于API的查询语言,是基于类型系统来进行查询的。
一个GraphQL服务是通过定义类型和类型上的字段来创建的,然后给每个类型的每个字段提供解析函数。
定义类型示例:
type Query {
me: User
}
type User {
id: ID
name: String
}
定义解析函数示例:
function Query_me(request) {
return request.auth.user;
}
function User_name(user) {
return user.getName();
}
一旦一个GraphQL服务端运行起来了,通常在web服务的一个URL上,这时它便可以接受GraphQL查询请求,并验证和执行。接收到的查询首先会被检查确保它只引用了已定义的类型和字段,然后运行指定的解析函数来生成结果。
客户端查询示例:
{
me {
name
}
}
服务器会返回的JSON效果
{
"me": {
"name": "Luke Skywalker"
}
}
二、查询和变更数据
1、字段fields
GraphQL 是请求对象上的特定字段。然后服务器会返回请求字段相同结构的数据
示例:请求
{
hero {
name
}
}
结果示例:
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
返回一个data的结构与请求的结构完全一致。
还可以对这个对象的字段进行次级选择(sub-selection)。GraphQL 查询能够遍历相关对象及其字段,使得客户端可以一次请求查询大量相关数据
次级选择请求示例:
{
hero {
name
# 查询可以有备注!
friends {
name
}
}
}
次级选择请求结果:
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
注意这个例子中,friends 返回了一个数组的项目,GraphQL 查询会同等看待单个项目或者一个列表的项目,然而我们可以通过 schema 所指示的内容来预测将会得到哪一种。
2、参数
每一个字段和嵌套对象都能有自己的一组参数
带参数的查询示例:
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
带参数查询结果示例:
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
}
}
3、别名
由于结果字段中并不包含参数,因此如果你的查询请求仅仅是参数不同,那么查询便会报错,因为他们的查询结果的结构是一样的。这就导致在data中数据无法显示。因此,我们需要给相同结构的查询不同的名称,以便于在查询结构中显示不同名称。示例
带别名的相同结构的查询:
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
带别名的相同结构的查询结果
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
上例中,两个 hero 字段将会存在冲突,但是因为我们可以将其另取一个别名,我们也就可以在一次请求中得到两个结果。
4、片段(fragment)
如果我们需要在不同的地方引用相同的查询,我们可以使用片段(fragment)
相同查询复用示例:
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
相同查询复用结果示例:
{
"data": {
"leftComparison": {
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
},
{
"name": "C-3PO"
},
{
"name": "R2-D2"
}
]
},
"rightComparison": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
5、操作名称
这之前,我们都使用了简写句法,省略了 query 关键字和查询名称,但是生产中使用这些可以使我们代码减少歧义。
操作类型可以是 query、mutation 或 subscription,描述你打算做什么类型的操作。操作类型是必需的,除非你使用查询简写语法,在这种情况下,你无法为操作提供名称或变量定义。
操作名称是你的操作的有意义和明确的名称。它仅在有多个操作的文档中是必需的,但我们鼓励使用它,因为它对于调试和服务器端日志记录非常有用。
操作名称查询示例:
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
查询结果示例:
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
6、变量
如果需要动态的向参数传递查询值,如下拉菜单选择后自动查询,这时就会用到变量,使用变量的步骤:
(1)使用$变量名代替原来的静态变量
(2)变量定义。$变量名:类型。在操作名称上面声明$变量名:类型为查询接受的变量之一。
(3)将 variableName: value传递进查询中
示例:
# { "graphiql": true, "variables": { "episode": JEDI } }
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
变量定义:变量定义可以是可选的或者必要的。上例中,Episode 后并没有 !,因此其是可选的。但是如果你传递变量的字段要求非空参数,那变量一定是必要的。
默认变量:
可以通过在查询中的类型定义后面附带默认值的方式,将默认值赋给变量。
示例:
query HeroNameAndFriends($episode: Episode = "JEDI") {
hero(episode: $episode) {
name
friends {
name
}
}
}
7、指令
如果我们希望能够动态的改变查询的结构,我们可以使用指令。
指令示例如下:
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
GraphQL 的核心规范包含两个指令,其必须被任何规范兼容的 GraphQL 服务器实现所支持:
@include(if: Boolean)仅在参数为true时,包含此字段。@skip(if: Boolean)如果参数为true,跳过此字段。
参数:
{
"episode": "JEDI",
"withFriends": false
}
查询结果:
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
8、变更(mutations)
修改数据示例:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
传入的参数:
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
返回的结果:
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
GraphQL可以在提交修改的时候同时提交查询,获得修改后的结果。当一个字段自增的时候,我们可以在一个请求中变更并查询这个字段的新值。
查询和变更多个字段的区别:
查询字段时,是并行执行,而变更字段时,是线性执行,一个接着一个。这意味着如果我们一个请求中发送了两个 incrementCredits 变更,第一个保证在第二个之前执行,以确保我们不会出现竞态。
9、内联片段
跟许多类型系统一样,GraphQL schema 也具备定义接口和联合类型的能力。如果你查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据
示例:查询
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
参数:
{
"ep": "JEDI"
}
结果:
{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
10、元字段
某些情况下,你并不知道你将从 GraphQL 服务获得什么类型,这时候你就需要一些方法在客户端来决定如何处理这些数据。GraphQL 允许你在查询的任何位置请求 __typename,一个元字段,以获得那个位置的对象类型名称。
查询示例:
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
查询结果:
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Han Solo"
},
{
"__typename": "Human",
"name": "Leia Organa"
},
{
"__typename": "Starship",
"name": "TIE Advanced x1"
}
]
}
}
上面的查询中,search 返回了一个联合类型,其可能是三种选项之一。没有 __typename字段的情况下,几乎不可能在客户端分辨开这三个不同的类型。
浙公网安备 33010602011771号