Javascript 为什么注册和我总是在变异和查询类型中?

Javascript 为什么注册和我总是在变异和查询类型中?,javascript,graphql,apollo-server,Javascript,Graphql,Apollo Server,我正在学习GraphQL,在我看到的每个示例中,注册/登录和me调用都在变异和查询类型中。为什么呢?例如: type Query { me: User } type Mutation { login(email: String!, password: String!): String } 这不应该是User类型吗?因为这是与用户相关的事情 如果这是一个基于观点的问题,我很抱歉,如果是这样的话,我将结束这个问题。这个问题基于GraphQL模式设计。设计是100%基于意见的东西。但让我试

我正在学习GraphQL,在我看到的每个示例中,
注册
/
登录
me
调用都在
变异
查询
类型中。为什么呢?例如:

type Query {
  me: User
}

type Mutation {
  login(email: String!, password: String!): String
}
这不应该是
User
类型吗?因为这是与用户相关的事情


如果这是一个基于观点的问题,我很抱歉,如果是这样的话,我将结束这个问题。

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

登录
应答中,客户机应收到身份验证令牌或类似信息。此
令牌将作为http头发送给每个身份验证请求。显然,此
令牌
不是
用户
实体的一部分。在复杂的应用程序中,您可以这样设计答案:

{
  user: ...
  token: ...
}
但对于演示应用程序,只需要令牌(字符串)作为
登录
变异答案。

来自:

GraphQL模型包含三种类型的操作:

  • 查询–只读获取

  • 突变–写入后接取

  • 订阅–一个长期请求,用于获取数据以响应源事件

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

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

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

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

query {
  me {
    # some set of User fields like id, firstName, etc.
  }
}
如果我们将
me
字段放在
User
类型上,a)我们不一定有方法请求该字段,因为它不在根目录下,b)我们将在返回
用户
的任何地方公开
me
字段(例如,返回
用户列表的
朋友
字段)这没什么意义

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

快速绕过突变: 如果您习惯于使用REST,则通常会看到以操作数据源为前缀的操作,例如:

POST /articles/{id}/bookmark
query ChangeMyEmail {
  me {
    changeEmail(email: "new@example.com")
    email  # is allowed to evaluate first and return old@example.com
  }
}
我们可能倾向于对我们的突变强制执行类似的结构,从而产生如下请求:

mutation {
  article {
    bookmark
  }
}
然而,这是完全没有必要的,而且事实上违反了惯例,使实现更加复杂。这样做就足够了:

mutation {
  bookmarkArticle
}

关于这一点,可以找到更多信息。

这是两个独立的问题

这里的第一个答案是,如果要进行GraphQL查询,则需要某种方法来获取对象以开始查询。没有像Java或其他面向对象语言中那样的“静态方法”概念;这些初始查询集必须位于根
Query
类型上。你可以想象

类型查询{
“当前登录的用户。”
我:用户!
“通过用户的电子邮件地址查找用户。”
用户(电子邮件:String!):用户
}
有了
User
对象后,您可以查询该对象上的其他字段,但最初需要某种方法来获取它

querywhatsmeyemail{
我{email}
}
其次,所有更改数据的操作都是
变异
类型的顶级字段。这主要是一种惯例——实际上没有什么能阻止您在解析器函数中做任何您想做的事情——但它有几个关键影响。只需查询对象上的每个字段并从模式中生成类似的查询就足够简单了:

User上的字段片段{
id、姓名、电子邮件、删除
}
查询WhoAmI{
me{…AllUserFields}#哎呀,我删除了自己
}
您还可以保证顶级突变按顺序执行,但GraphQL中的所有其他内容都可以按任何顺序执行。如果您的模式允许,则
电子邮件可以是旧的或新的电子邮件地址,例如:

POST /articles/{id}/bookmark
query ChangeMyEmail {
  me {
    changeEmail(email: "new@example.com")
    email  # is allowed to evaluate first and return old@example.com
  }
}
像“登录”这样实际上只是“操作”的东西,不与任何特定对象绑定,只附加到
Mutation
,而不是任何更特定的类型,这更有意义。(如果“login”是
User
的一个属性,那么是哪一个属性?如果尚未登录,您如何查找该属性?)