7. 定义查询
在之前的课程中,我们只是对已经定义的schema调用查询。 因为当时我们的目标是掌握查询语言,所以对schema并不关注。
现在,我们已经对查询语言有了更多的了解,到了开始编写GraphQL schema的最佳时机啦!
让我们开始吧!
准备工作
首先,需要下载GraphQL Sandbox
项目并在本地运行它,我们将在这个项目里定义schema。
复制repo到本地:
git clone https://github.com/kadirahq/graphql-blog-schema.git
切换到build-schema
分支:
cd graphql-blog-schema
git checkout build-schema
安装依赖:
npm install
启动沙盒:
npm start
现在就可以通过这个地址http://localhost:3000访问沙盒项目了。
尽情折腾它吧:)
检查schema
GraphQL Sandbox有一个schema,它包含一个简单的查询字段称为echo
。 echo接受一个消息(message)作参数并返回它。
运行下这个查询吧:
{
receivedMessage: echo(message: "Hello")
}
得到的结果:
{
"data": {
"receivedMessage": "received Hello"
}
}
现在,让我们来仔细研究一下这个schema。首先,在刚刚下载到本地的‘graphql-blog-schema’项目中找到shema的定义文件,然后用编辑器打开
* 文件: `src/schema.js`
schema.js是一个‘自注释’的文件,基于ES2015开发,还使用了它的module语法。
schema的定义在文件的结尾处:
const Schema = new GraphQLSchema({
query: Query
});
这里创建了一个GraphQL schema对象示例,并注册了Query
(通常也称Query
为根查询)。这个schema就是我们在GraphQL沙盒
的文档选项卡中看到的blogschema
对象。 现在让我们看看这个Query
对象中有些什么呢。
const Query = new GraphQLObjectType({
name: 'BlogSchema',
description: "Root of the Blog Schema",
fields: () => ({
echo: {
type: GraphQLString,
description: "Echo what you enter",
args: {
message: {type: GraphQLString}
},
resolve: function(root, {message}) {
return `recieved ${message}`;
}
}
})
});
我们实例化了一个GraphQLObjectType对象,命名为BlogSchema
,
对它的类型进行了描述,还定义了几个字段。
一个是echo
字段,它是个字符串(GraphQLString)。
在
schema.js
文件的开头列了GraphQL里定义的常见类型。
echo
字段接收一个叫消息(message)的参数。
也可以为
message
添加描述,如下所示:
{
...
args: {
message: {type: GraphQLString, description: "Give me a message"}
}
...
}
接下来是resolve
方法,我们将在这个方法里实现query的处理逻辑并返回结果。
{
...
resolve: function(root, args) {
return `recieved ${args.message}`;
}
...
}
resolve
方法的第二个参数对应的是query中定义的args
字段。
以上,就是开发一个简单的GraphQL schema所需了解的全部知识啦
现在,有一小任务,试试通过resolve
方法返回下面的对象
{aa: 10};
之后调用查询来获取echo字段的值,会是下面的哪一个结果呢?
- {aa: 10}, // Object
- {"aa": 10} // JSON string
- [object Object]
- There was an error.
得到的结果为[object Object]。 想想看是为什么呢?
echo
是字符串类型,所以GraphQL会尝试从resolve方法的返回值里获取其字符串表达。通常来说,所有对象类型转为字符串后都会得到[object Object]这个值。正如上面看到的结果。
我们可以通过设置不同类型返回其他的值,观察结果有何不同。同时,尝试更改echo的类型,进一步了解看看它是如何工作的。
定义贴子(post)类型
现在需要定义一个实际的类型了,并写出正确的根查询字段。让我们一起实现博客系统里的这个Post
类,并创建根查询字段posts
吧。
下面是Post
类的最精简版本。
const Post = new GraphQLObjectType({
name: "Post",
description: "This represent a Post",
fields: () => ({
_id: {type: new GraphQLNonNull(GraphQLString)},
title: {type: new GraphQLNonNull(GraphQLString)},
content: {type: GraphQLString}
})
});
在该类型里,我们指定_id
和title
为必须字段,content
为可选字段。GraphQL中的所有字段都是可选的,如果想将一个字段定义为必须字段,需要对它进行明确的标识。
将此类型添加到我们的schema.js
文件。
把Post放在根查询的定义上方。
现在我们可以在根查询里创建posts
字段啦,如下代码所示:
const Query = new GraphQLObjectType({
name: 'BlogSchema',
description: "Root of the Blog Schema",
fields: () => ({
posts: {
type: new GraphQLList(Post),
resolve: function() {
return PostsList;
}
}
})
});
posts
字段返回一个帖子的列表,列表里的每一项都是Post
类型。resolve函数中返回了一个PostsList
,我们的帖子都存在这个数组里。postsList的定义在src/data/posts.js
文件中,你可以研究研究。
以下是添加这些更改后,
schema.js
文件的内容:https://gist.github.com/arunoda/2cbba32de83bfa96099d
现在让我们在本地运行GraphQL Sandbox,然后试试查询posts
字段。
在编辑schema.js文件后,通常需要重新加载http://localhost:3000。 否则,自动填充功能可能无法正常工作。
做个简单的任务练练手。不用PostsList
而是通过resolve
方法返回下方的值。
[{_id: "some-id"}]
现在尝试调用以下查询:
{
posts: posts {
title
}
}
你会得到什么结果呢?
- Empty value for the title.
- Error as: Cannot return null for non-nullable field Post.title .
- Error as: Needs Post.title in the return value.
- "null" value for the title.
定义默认值
你会收到一条错误消息,“Cannot return null for non-nullable field Post.title”。 因为我们将title
定为必须字段。
因为数据集存在不稳定的情况,所以在我们的数据里,很可能有一些标题为空的帖子。但因此抛出一个错误似乎不是很合适。
针对这种情况,通常我们有两个做法:一是将标题设为可选字段;二是为类型定义默认值。
现在,我们依然保留title
为必须字段。然后在它没有值的时,为其添加一个默认值。
const Post = new GraphQLObjectType({
name: "Post",
description: "This represent a Post",
fields: () => ({
_id: {type: new GraphQLNonNull(GraphQLString)},
title: {
type: new GraphQLNonNull(GraphQLString),
resolve: function(post) {
return post.title || "Does not exist";
}
},
content: {type: GraphQLString}
})
});
在上面的代码中,我们专门为title
字段定义了一个resovle
函数,通过它来为来为title
赋值。
resolve函数的第一个参数是个对象,它的值是PostsList
数组中的一个。
现在我们可以自定义数据源中的值了,并返回所需的任何东西。
定义嵌套字段
现在我们尝试在schema中定义一个嵌套字段。 例如,为Post
类添加一个author
字段。
为此,首先我们需要创建一个Author
类。
const Author = new GraphQLObjectType({
name: "Author",
description: "This represent an author",
fields: () => ({
_id: {type: new GraphQLNonNull(GraphQLString)},
name: {type: GraphQLString}
})
});
然后在Post
类中定义author
。
const Post = new GraphQLObjectType({
name: "Post",
description: "This represent a Post",
fields: () => ({
...
author: {
type: Author,
resolve: function(post) {
return AuthorsList.find(a => a._id == post.author);
}
}
})
});
在PostsList
数组中,每个post对象都有一个"author"字段,它的值是author的_id
,一个字符串。所有的auhtorid
都存储在AuthorsList
中。
因此,在author
的resolve函数里,会根据这个id找到正确的author
然后返回结果。 同时,我们也将author
字段的类型定义为Author
。
下面是schema.js文件的最终版:https://gist.github.com/arunoda/c29128be2c5e979475ec.
查询下面的query:
{
posts {
title,
author {
name
}
}
}
作者“Kasun Indi”创建了多少帖子:
- 1
- 2
- 3
- 4
最后
现在,你知道如何定义GraphQL schema并解析字段了,同时,我们还讨论了如何编写嵌套的字段。这样,你就可以根据需要定义任意数量的嵌套层级了。
这就是将数据集构建为图形的方法。我们将在接下来的课程中进一步探讨GraphQL schema。