为什么 signup 和 me 总是在 mutation 和 query 类型中?

Why the signup and me are always in the mutation and query types?

我正在学习 GraphQL,在我看到的每个示例中,signup/loginme 调用在 mutationquery 类型。这是为什么?示例:

type Query {
  me: User
}

type Mutation {
  login(email: String!, password: String!): String
}

这不应该是User类型吗?既然是用户相关的东西?

如果这是一个基于意见的问题,我深表歉意 - 如果是这样,我将关闭该问题。

本题基于GraphQL模式设计。设计是 100% 基于意见的东西。但让我试着回答你的问题。

login 变异答案中,客户端应该收到授权令牌或类似的。此 token 将作为 http header 发送到每个身份验证请求。显然,这个 token 不是 User 实体的一部分。在复杂的应用程序中,您可以像这样设计变异答案:

{
  user: ...
  token: ...
}

但对于演示应用程序,只需要令牌(字符串)作为 login 变异答案。

来自spec

There are three types of operations that GraphQL models:

  • query – a read‐only fetch.

  • mutation – a write followed by a fetch.

  • subscription – a long‐lived request that fetches data in response to source events.

每个操作都与特定类型相关联,尽管突变和订阅类型都是可选的。所以模式可以这样定义:

schema {
  query: Query
}

这里,Query指的是一个名为Query的类型。按照惯例,这三种类型被命名为 QueryMutationSubscription——但它们可以命名为任何其他名称,这并不重要。但是,它们必须是对象类型并且必须至少包含一个字段。

这些操作类型充当 GraphQL 操作的“根”,实际上是数据图的入口点。没有其他类型在根级别公开,即使它们存在于您的模式中。

meviewer 字段通常在 Query 类型上公开,以允许用户获取当前登录的用户。假设我们已经定义了一个 User 类型,如果我们向 Query 类型添加一个 me 字段,并将该字段的类型设置为 User,我们将启用消费者我们的端点来编写如下查询:

query {
  me {
    # some set of User fields like id, firstName, etc.
  }
}

如果我们将 me 字段改为 User 类型,a) 我们不一定有办法请求该字段,因为它不在根目录中,并且 b ) 我们会在 return 一个 User 的任何地方公开一个 me 字段(例如,一个 return 是 Users 列表的 friends 字段)这没有多大意义。

相同的逻辑适用于 login 之类的突变——我们希望它们位于操作的根部,因此我们将它们放在 Mutation 类型中。另一方面,login 应该是突变还是查询,.

快速绕过突变:

如果您习惯于使用 REST,通常会看到操作以它们正在操作的数据源为前缀,例如:

POST /articles/{id}/bookmark

而且我们可能倾向于用我们的突变来强制执行类似的结构,从而导致这样的请求:

mutation {
  article {
    bookmark
  }
}

然而,这是完全没有必要的,而且实际上破坏了惯例并且使实现更加复杂。这样做就足够了:

mutation {
  bookmarkArticle
}

有关这一点的更多信息,请参见

原来这是两个不同的问题。

这里的第一个答案是,如果您要进行 GraphQL 查询,则需要某种方法让对象开始查询。没有像您在 Java 或其他面向对象语言中看到的那样的 "static methods" 概念;这些初始查询集必须在根 Query 类型上。你可以想象

type Query {
  "The currently logged-in user."
  me: User!

  "Find a user by their email address."
  user(email: String!): User
}

您可以在 User 对象上查询其他字段,但您需要一些方法来初始获取它。

query WhatsMyEmail {
  me { email }
}

其次,所有更改数据的操作都是Mutation类型上的顶级字段。这主要是约定俗成——实际上 没有什么能阻止你在解析器函数中做任何你想做的事——但它有几个关键影响。只需查询对象上的每个字段并从模式中生成这样的查询就足够容易了:

fragment AllUserFields on User {
  id, name, email, delete
}
query WhoAmI {
  me { ...AllUserFields } # oops I deleted myself
}

您还可以保证顶级变更按顺序执行,但 GraphQL 中的其他所有内容都可以按任何顺序执行。如果您的模式允许这样做,email 可以是旧电子邮件地址或新电子邮件地址,例如:

query ChangeMyEmail {
  me {
    changeEmail(email: "new@example.com")
    email  # is allowed to evaluate first and return old@example.com
  }
}

像 "login" 这样的东西实际上只是 "actions",没有绑定到任何特定的对象,更有意义的是只附加到 Mutation 而不是任何更具体的类型。 (如果 "login" 是 User 的 属性,是哪一个,你如何在没有登录的情况下找到它?)