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访问沙盒项目了。

img

尽情折腾它吧:)


检查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}
  })
});

在该类型里,我们指定_idtitle为必须字段,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。

results matching ""

    No results matching ""