查询与修改
# Query
从这里开始就准备开始接触实际的操作了,Query
就对比于RESTful
来说就类似于Router
,它是作为入口提供给客户端调用查询数据的。
我们再来看看详细的定义方式:
type Account {
name: String
age: Int,
sex: String,
salary(city: String): Int
}
type Query {
name: String
age: Int,
account(username: String!): Account
accounts: [Account]
}
2
3
4
5
6
7
8
9
10
11
12
13
我们这里可以先忽略字段后面括号内容,这个属于
Argument
的内容,详细可以参考Argument
部分。
从代码中来看,这里定义了Query
类型,它是作为查询入口而存在的,同时这个对象中定义了多个字段,这些字段将暴露给客户端进行查询返回,并且这些字段不限于标量类型联合类型等等,不仅如此,定义在Query
中的这些字段都要有他们各自的resolver
,因为这里只是定义了他们的结构,具体返回值和逻辑部分需要有特定的resolver
来处理,具体参考下面resolver
部分。
那么客户端又是怎么做到针对这些字段进行查询的呢?其实也很简单,直接按照他的结构进行一一对应查询即可,具体实现如下:
query {
account(username: "小李子") {
name
sex
age
}
age,
name
}
2
3
4
5
6
7
8
9
如上所示,我们可以按照我们想要的字段进行查询,我们需要什么字段就在对应的结构中写上字段名,那么服务器接收到请求之后就会按照你想要的仅仅返回你定义的查询字段,其他的都不会返回。
这里有一个注意点,如果你的目标对象不是原始类型,而是一个对象,那么你必须标识你需要这个对象上的特定字段,也就是说必须精确到标量类型,否则就会出错,比如上面的account
字段(这里忽略传参逻辑,后面会说),这个account
对象定义的类型是Account
,而Account
类型具有name
、age
、sex
、salary
字段,那你在查询的时候,必须指定这个字段对象中的值,如果这个值还是对象,那你就还必须循环上述步骤直至拿到标量类型为止。
# Argument
关于参数,我们应该也很熟悉了,不就是类似于函数一样的传参。话虽如此,但是这里还是有点区别的。
我们从代码层次来看GraphQL
中怎么实现传参的:
{
salary(city: String!): Int
}
2
3
首先上面这个是在服务端定义结构的时候的写法,也比较好理解,也就是想要获取这个字段的值,那么你就必须传递这个参数(因为加了!,所以必须传),具体的这个参数怎么接收怎么处理,Resolver
部分会提到,然后我们再来看客户端怎么进行查询传参的:
query {
salary(city: "上海")
}
2
3
是不是也很好理解,只要参数名与你在服务端定义的参数名一致就可以了,然后后面给定具体的值。
# Resolver
对于GraphQL
来说,它只有一个入口点,也就是根节点,查询一般都是从这个Root
开始。
假设有这样一个Schema
:
type UserInfo {
name: String
age: Int
}
type Query {
userinfo(userId: Int): UserInfo
}
2
3
4
5
6
7
而对应的客户端查询语句如下:
query {
userinfo(userId: 666): {
name
age
}
}
2
3
4
5
6
为了正常的响应给客户端这些字段的数据,那么我们就需要给特定的字段编写它的resolver
。比如,上述的userinfo
对应的实际数据其实就是由一个resolver
进行处理返回的,一个resolver
一般是一个对应的是一个函数,参数分别为:
obj
代表上一个对象;args
查询参数;context
上下文,类似于Koa
的ctx
;
那么如何编写上述userinfo
字段的resolver
呢:
const root = {
userinfo: function(obj, args, context) {
const userId = args.userId;
return {
name: '李明' + userId,
age: 18
}
}
}
2
3
4
5
6
7
8
9
这里忽略了这些数据的获取方式,直接写死的,而在实际项目中,这些字段的数据来源可以是数据库或者是其他RPC
调用等等方式。同时我们应该也能注意到,我们在对需要传参的字段做处理的时候,直接可以从args
中拿到对应的参数值,然后做逻辑处理返回。
在编写完resolver
后,这个字段才能在查询的时候拿到对应的值。
# Mutation
在讲解完Query
和Resolver
之后,相信大家对基本的查询交互代码编写已经有所了解,那么现在就继续讲解怎么修改数据吧。
在开始讲解修改数据之前我们需要想想,如果我们需要修改某个用户的信息,我们是不是需要把新信息作为参数传递过去,那么这个参数类型势必就不是一个标量类型能够表示的了,所以就需要用到我们上面提到过的输入类型来进行传参。
# 定义输入类型传参
首先先定义schema
type Account {
name: String
age: Int,
sex: String,
salary(city: String): Int
}
input AccountInput {
name: String
age: Int,
sex: String
}
type Mutation {
createAccount(input: AccountInput): Account
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里与
Query
不同的是,它的名字需要改成Mutation
,Query
与Mutation
这两个名字是指定的,不能更改。
我们从代码上看,其实和Query
传参区别不大,唯一的区别在于参数的类型由之前的String
变成现在了AccountInput
,也就是从一个标量类型变成了一个对象类型,而定义输入类型的方式也从type
变成了input
。
然后再来看看客户端传参方式:
mutation {
createAccount(input: {
name: "李四"
age: 10
sex: "女"
}) {
name
}
}
2
3
4
5
6
7
8
9
其实这里和上述的传参方式没啥区别,唯一的不同就是类型的转变,这里变成了对象方式的传参。