Единственная реальная разница между запросами и мутациями заключается в том, что если одна операция включает в себя несколько мутаций, они разрешаются последовательно (по одной за раз), а не одновременно. Запросы и все остальные поля разрешаются одновременно. Это означает, что для такой операции:
mutation myOperation {
editComment(id: 1, body: "Hello!")
deleteComment(id: 1)
}
Мутация editComment
разрешится до мутации deleteComment
. Если бы эти операции были запросами, они выполнялись бы одновременно. Аналогично, подумайте, есть ли у вас мутация, которая возвращает объект, например:
mutation myOperation {
deleteComment(id: 1) {
id
name
}
}
В этом случае поля id
и name
также разрешаются одновременно (поскольку, даже если они возвращаются как часть мутации, сами поля не являются мутациями) .
Эта разница в поведении между запросами и мутациями подчеркивает, почему по соглашению мы определяем одну мутацию для каждой операции и избегаем "вложенных" мутаций, как предполагает ваш вопрос.
Ключ к тому, чтобы сделать ваши мутации более гибкими, заключается в том, как впоследствии вы вводите входные данные для своей мутации, как вы обрабатываете эти входные данные внутри вашего резольвера. Вместо того, чтобы делать мутацию deleteAllUserPostCommentsByForum
, просто сделайте мутацию deleteComments
, которая принимает более надежный InputType, например:
input DeleteCommentsInput {
forumId: ID
userId: ID
}
Тогда вашему преобразователю просто нужно обработать любую комбинацию полей ввода, которые могут быть переданы. Если вы используете db, этот тип ввода очень легко преобразуется в предложение WHERE
. Если вы понимаете, что вам нужны дополнительные функции, например, удаление комментариев до или после определенной даты, вы можете добавить эти поля в ваш тип ввода и соответственно изменить свой преобразователь - не нужно создавать новую мутацию.
На самом деле вы можете обрабатывать создание и редактирование одинаково и делать вещи немного СУХИМ. Например, ваша схема может выглядеть так:
type Mutation {
createOrUpdateComment(comment: CommentInput)
}
input CommentInput {
id: ID
userId: ID
body: String
}
Ваш распознаватель может затем проверить, был ли включен идентификатор - если это так, то он обрабатывает операцию как обновление, в противном случае он обрабатывает операцию как вставку. Конечно, использование ненулевых значений в этом случае может быть сложным (userId
может потребоваться для создания, но не для обновления), поэтому есть что сказать, чтобы иметь отдельные типы ввода для каждого вида операции. Однако, надеюсь, это все еще иллюстрирует, как вы можете использовать типы ввода, чтобы сделать ваши мутации более гибкими.