Я изучаю GraphQL, и в КАЖДОМ примере, который я видел, вызовы signup/login и me относятся к типам mutation и query. Это почему? Пример:
type Query {
me: User
}
type Mutation {
login(email: String!, password: String!): String
}
Разве это не должно быть в типе User? Поскольку это связано с пользователем?
Я извиняюсь, если это вопрос, основанный на мнении, и я закрою вопрос, если это так.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Этот вопрос основан на дизайне схемы GraphQL. Дизайн на 100% основан на мнении. Но позвольте мне попробовать ответить на ваш вопрос.
В ответе на мутацию login клиент должен получить токен авторизации или аналогичный. Этот token будет отправляться как HTTP-заголовок при каждом запросе на авторизацию. Очевидно, что этот token не является частью User сущности. В сложном приложении вы можете разработать ответ на мутацию следующим образом:
{
user: ...
token: ...
}
Но для демо-приложения нужен только токен (строка) в качестве ответа на мутацию login.
Из спец.:
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. По соглашению эти три типа названы Query, Mutation и Subscription, но их можно назвать как угодно, это не имеет большого значения. Однако они должны быть объектными типами и должны включать по крайней мере одно поле.
Эти типы операций служат «корнем» операции GraphQL и фактически являются точками входа в ваш граф данных. Никакие другие типы не отображаются на корневом уровне, даже если они существуют в вашей схеме.
Поле me или viewer часто отображается в типе Query, чтобы пользователи могли получить текущего пользователя, вошедшего в систему. Предполагая, что у нас уже есть определенный тип User, если мы добавим поле me к нашему типу Query и установим для этого поля тип User, мы позволим потребителям нашей конечной точки написать запрос, например:
query {
me {
# some set of User fields like id, firstName, etc.
}
}
Если вместо этого мы поместим поле me в тип User, а) у нас не обязательно будет способ запросить поле, поскольку оно не будет в корне, и б) мы будем отображать поле me везде, где мы возвращаем User ( например, поле friends, которое возвращает список Users), что не имеет особого смысла.
Та же логика применима к мутациям вроде login — мы хотим, чтобы они были в корне нашей операции, поэтому мы помещаем их внутрь типа Mutation. С другой стороны, должен ли login быть мутацией или запросом, субъективен и зависит от вашего конкретного варианта использования.
Если вы привыкли работать с REST, часто можно увидеть операции с префиксом источников данных, с которыми они работают, например:
POST /articles/{id}/bookmark
И мы можем быть склонны применять аналогичную структуру с нашими мутациями, что приводит к таким запросам, как:
mutation {
article {
bookmark
}
}
Однако это совершенно не нужно, фактически нарушает соглашение и усложняет реализацию. Вместо этого достаточно сделать:
mutation {
bookmarkArticle
}
Подробнее об этом можно узнать здесь.
Получается два отдельных вопроса.
Первый ответ здесь: если вы делаете запрос GraphQL, вам нужен какой-то способ заставить объекты начать запрашивать. Нет понятия "статические методы", как в Java или других объектно-ориентированных языках; этот набор начальных запросов должен быть корневого типа 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: "[email protected]")
email # is allowed to evaluate first and return [email protected]
}
}
Такие вещи, как «логин», которые на самом деле являются просто «действиями», не привязанными к какому-либо конкретному объекту, имеют больше смысла просто привязываться к Mutation, а не к какому-либо более конкретному типу. (Если «логин» был свойством User, то каким, и как его найти, не входя в систему?)