Не могли бы вы объяснить, почему, если входной аргумент мутации является объектом, он должен быть типа ввода? Я думаю, что гораздо проще просто повторно использовать тип без предоставления идентификатора.
Это нормально для небольшого объекта, но когда у вас есть много объектов с 10 + свойствами в схеме, которые станут обузой.
Ответ 2
Из спецификации:
Тип объекта GraphQL (ObjectTypeDefinition)... не подходит для повторного использования [в качестве входных данных], поскольку типы объектов могут содержать поля, которые определяют аргументы или содержат ссылки на интерфейсы и объединения, ни один из которых не подходит для использования в качестве входного аргумента, По этой причине входные объекты имеют отдельный тип в системе.
Это "официальная причина", но есть несколько практических причин, по которым вы не можете использовать тип объекта в качестве типа входного объекта или использовать тип объекта в качестве типа входного объекта:
функциональность
Типы объектов и типы входных объектов имеют поля, однако эти поля имеют разные свойства, которые отражают, как эти типы используются схемой. Ваша схема потенциально может определять аргументы и некую функцию распознавателя для полей типа объекта, но эти свойства не имеют смысла во входном контексте (то есть вы не можете разрешить поле входного объекта - оно уже имеет явное значение), Аналогично, значения по умолчанию могут быть предоставлены только для полей типа входного объекта, но не для полей типа объекта.
Другими словами, это может показаться дублированием:
type Student {
name: String
grade: Grade
}
input StudentInput {
name: String
grade: Grade
}
Но добавление функций, характерных либо для типов объектов, либо для типов входных объектов, дает понять, что они ведут себя по-разному:
type Student {
name(preferred: Boolean): String
grade: Grade
}
input StudentInput {
name: String
grade: Grade = F
}
Тип системы ограничения
Типы в GraphQL сгруппированы по типам вывода и типам ввода.
Типы вывода - это типы, которые могут быть возвращены как часть ответа, выданного службой GraphQL. Типы ввода - это типы, которые являются допустимыми входными данными для аргументов поля или директивы.
Там пересекаются между этими двумя группами (то есть скаляры, перечисления, списки и ненулевые). Однако абстрактные типы, такие как объединения и интерфейсы, не имеют смысла во входном контексте и не могут использоваться в качестве входных данных. Разделение типов объектов и типов входных объектов позволяет гарантировать, что абстрактный тип никогда не используется там, где ожидается входной тип.
Схема дизайна
При представлении сущности в вашей схеме вполне вероятно, что некоторые сущности действительно будут "разделять поля" между своими соответствующими типами ввода и вывода:
type Student {
firstName: String
lastName: String
grade: Grade
}
input StudentInput {
firstName: String
lastName: String
grade: Grade
}
Однако типы объектов могут (и в действительности часто делают) моделировать очень сложные структуры данных:
type Student {
fullName: String!
classes: [Class!]!
address: Address!
emergencyContact: Contact
# etc
}
Хотя эти структуры могут преобразовываться в соответствующие входные данные (мы создаем Студента, поэтому мы также передаем объект, представляющий их адрес), часто они этого не делают - т.е., Возможно, нам нужно указать классы студентов по идентификатору класса и идентификатору раздела, а не объект. Точно так же у нас могут быть поля, которые мы хотим вернуть, но не хотим изменять, или наоборот (например, поле password
).
Более того, даже для относительно простых объектов мы часто предъявляем различные требования в отношении обнуляемости между типами объектов и их "двойниками" входных объектов. Часто мы хотим гарантировать, что поле также будет возвращено в ответе, но мы не хотим делать те же поля обязательными для нашего ввода. Например,
type Student {
firstName: String!
lastName: String!
}
input StudentInput {
firstName: String
lastName: String
}
Наконец, во многих схемах часто не существует взаимно-однозначного сопоставления между типом объекта и типом входного объекта для данной сущности. Распространенным шаблоном является использование отдельных типов входных объектов для различных операций для дальнейшей тонкой настройки проверки ввода на уровне схемы:
input CreateUserInput {
firstName: String!
lastName: String!
email: String!
password: String!
}
input UpdateUserInput {
email: String
password: String
}
Все эти примеры иллюстрируют важный момент - хотя тип входного объекта может время от времени отражать тип объекта, гораздо меньше шансов увидеть это в производственных схемах из-за требований бизнеса.