如果你在你的生产应用中使用了GraphQL,你可能遇到过这样的情况:你需要你的自定义查询来返回自定义字段名。或者,也许你在同一个服务器响应中收到了两个查询结果,导致字段名发生冲突。
在这些情况下,我们可以使用GraphQL别名来改进我们的查询。在本教程中,我们将介绍什么是别名,它是如何工作的,以及何时应该使用它。我们将研究几种情况,并通过一个相关的例子来扩展这个概念。让我们开始吧!
什么是GraphQL别名?
别名允许我们重新命名查询结果中返回的数据。别名不会改变原来的模式,相反,它们会操纵从数据库中获取的查询结果的结构,根据你的规范来显示它。
当你想提高你的查询效率时,别名就会很方便。例如,假设你试图通过应用各种过滤器来获取相同的数据。许多开发者会倾向于写单独的查询,并在独立的API调用中执行它们。在这种情况和其他情况下,别名可以帮助你优化和改善你的查询组织。
有几种情况下,你可能想改变你接收查询结果的字段的名称。让我们看一下两个不同的例子。
在一个查询中获取多个对象
使用别名,你可以在一个单一的GraphQL查询中结合对同一对象的多次获取。
比方说,你正在建立一个应用程序,在一个feed中显示一个帖子列表。在你的应用程序中,一个典型的帖子看起来像下面的代码块。
type Post {
id: string
parent: string
type: string # POST or COMMENT
author: string
title: string
text: string
createdAt: string
}
为了简单起见,我们将自己限制在每个字段的字符串数据类型上。理想情况下,你会为type 字段使用一个枚举,为createdAt 字段使用一个自定义日期标量。
请注意,我们定义了一个type 字段,以适应同一表格中不同类型的帖子。在我们的例子中,我们有帖子和评论。
getPosts 的查询看起来像下面的代码块。
query getPosts {
posts {
id
parent
type
author
title
text
createdAt
}
}
现在,让我们假设你的数据库中有一个帖子和一个评论。如果你运行上面的查询,你会得到一个类似于下面代码块的响应。
{
"data": {
"posts": [
{
"id": "s9d8fhsd-fsdf",
"parent": null,
"type": "POST",
"author": "Korg",
"title": "Let's start a revolution",
"text": "Hey, man. I'm Korg. We're gonna get outta here on that big spaceship. Wanna come?",
"createdAt": "11:06 AM IST, Aug 7 2021"
},
{
"id": "d8g6dffd-jfod",
"parent": "s9d8fhsd-fsdf",
"type": "COMMENT",
"author": "Loki",
"title": null,
"text": "Well, it seems that you are in dire need of leadership.",
"createdAt": "11:09 AM IST, Aug 7 2021"
}
]
}
}
你的数据库也可能包含一长串的帖子和评论,就像上面的例子。你可能会尝试写一个查询,在一个分离的帖子和评论列表中获取这些帖子。
query getPosts {
posts(type: "POST") {
id
author
title
text
createdAt
}
posts(type: "COMMENT") {
id
parent
author
text
createdAt
}
}
然而,你会发现,这个查询根本没有运行,它抛出了以下错误。
{
"errors": [
{
"message": "Fields 'posts' conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional.",
"locations": [
{
"line": 2,
"column": 3
},
{
"line": 9,
"column": 3
}
]
}
]
}
当执行时,该查询为同一个字段名返回两个结果列表posts 。使用别名,我们可以将字段名改为两个不同的关键字,使查询完美地运行。
query getPosts {
posts: posts(type: "POST") {
id
author
title
text
createdAt
}
comments: posts(type: "COMMENT") {
id
parent
author
text
createdAt
}
}
现在,你可以很容易地在同一个GraphQL查询中进行多次获取调用,节省网络使用,降低代码的复杂性。
为你的查询结果增加意义
一个不言自明的名字让任何开发者都能很好地理解你的代码。对于数据库结果来说也是如此。你需要确保处理API响应的开发人员熟知数据的含义。让我们学习如何为我们的查询结果添加意义,以帮助提供有用的命名规则。
让我们重新考虑一下我们先前的例子。我们的模式看起来像下面的代码块。
type Post {
id: string
type: string # POST or COMMENT
author: string
title: string
text: string
createdAt: string
}
该模式包含一个父字段,旨在将一个评论与一个帖子联系起来,定义哪个帖子是评论的父。虽然这对数据库工程师来说应该是有意义的,但对前端工程师来说,可能不是马上就能明白。
在这种情况下,你可以构造你的传统查询,以更好地描述父属性。
query getPosts {
posts {
id
parentPost: parent
type
author
title
text
createdAt
}
}
你可以按照你的意愿重命名其他属性。在执行查询后返回的结果中,原始名称将被完全覆盖。一个典型的响应将看起来像下面的代码块。
{
"data": {
"posts": [
{
"id": "s9d8fhsd-fsdf",
"parentPost": null,
"type": "POST",
"author": "Korg",
"title": "Let's start a revolution",
"text": "Hey, man. I'm Korg. We're gonna get outta here on that big spaceship. Wanna come?",
"createdAt": "11:06 AM IST, Aug 7 2021"
},
{
"id": "d8g6dffd-jfod",
"parentPost": "s9d8fhsd-fsdf",
"type": "COMMENT",
"author": "Loki",
"title": null,
"text": "Well, it seems that you are in dire need of leadership.",
"createdAt": "11:09 AM IST, Aug 7 2021"
}
]
}
}
GraphQL别名的最佳实践
虽然在GraphQL中实现别名是相当直接的,但不恰当的使用会给你的应用程序带来巨大的破坏。为了优化GraphQL别名的结果,一定要牢记以下几点。
不言自明的命名
因为别名允许你改变你的结果的字段名,所以必须确保你选择的名字对实际数据有意义。
尽管选择一个方便的、简短的、你容易理解的名字是很诱人的,但你也必须考虑到其他开发者可能会在你之后从事这个项目。除非你的命名约定是不言自明的,否则他们可能很难理解你的代码,有可能在你的项目中造成错误。
例如,下面的代码就包含了一个命名不当的别名的例子。
query getPosts {
p: posts(type: "POST") {
id
author
title
text
createdAt
}
c: posts(type: "COMMENT") {
id
parent
author
text
createdAt
}
}
将两个中间查询结果命名为p ,以表示帖子,而将评论命名为c ,这似乎很方便,然而,让我们看看上述查询的结果。
{
"data": {
"p": [
{
"id": "s9d8fhsd-fsdf",
"author": "Korg",
"title": "Let's start a revolution",
"text": "Hey, man. I'm Korg. We're gonna get outta here on that big spaceship. Wanna come?",
"createdAt": "11:06 AM IST, Aug 7 2021"
},
],
"c": [
{
"id": "d8g6dffd-jfod",
"parent": "s9d8fhsd-fsdf",
"author": "Loki",
"text": "Well, it seems that you are in dire need of leadership.",
"createdAt": "11:09 AM IST, Aug 7 2021"
}
]
}
}
正如你所看到的,如果没有关于结果类型的上下文信息,就很难理解p 和c 是什么意思。因此,在这种情况下,坚持使用全名posts 和comments 将是一个更好的选择。
只在需要时使用别名
别名只是一个现有字段的假名,所以即使不一定需要,也可能很想使用它们。然而,重命名会导致你的代码中出现不必要的映射。例如,让我们以我们的getPosts 查询为例。
query getPosts {
posts {
id
parent
type
author
title
text
createdAt
}
}
posts 定义中的每个字段都是不言自明的。然而,你可能想为每个字段添加一个更有力的描述符,如下所示。
query getPosts {
posts {
postId: id
parentPost: parent
postType: type
author
title
postContent: text
createdAt
}
}
请记住,每次重命名时,你都需要在你的代码中传播新的名称。在添加别名之前,你应该考虑额外的细节是否有用。我建议只有在解决了一个反复出现的问题时才使用别名。
进行服务器端修改
如果你发现自己反复重命名相同的字段,你可能要考虑放弃别名,在服务器本身重命名该字段。同样,如果你不得不在你的客户端频繁使用别名,你可能需要减少你的数据库模式。
别名是为了方便快速重命名,然而,你不应该严重依赖它们。如果你发现自己经常使用别名,那么你的服务器端的命名规则可能有问题。在这种情况下,使用别名只会使代码库更加复杂。
总结
别名是重新组织GraphQL查询结果的好方法。当你的后端数据模型和前端数据规范不完全吻合,你需要手动调整它们时,别名尤其有用。
当你需要在同一来源上一次性获取多个查询结果时,别名是必须的。然而,少用别名也是必要的。虽然别名大多是无害的,而且只在数据被返回后才会改变,但不必要的使用会导致数据模式的混乱,甚至导致错误。
我希望你喜欢这个教程
在GraphQL中使用别名一文首次出现在LogRocket博客上。