Pourriez-vous expliquer pourquoi si l'argument d'entrée de la mutation est un objet, il devrait être type d'entrée? Je pense que beaucoup plus simple réutilise simplement type sans fournir d'id.
Par exemple:
type Sample {
id: String
name: String
}
input SampleInput {
name: String
}
type RootMutation {
addSample(sample: Sample): Sample # <-- instead of it should be
addSample(sample: SampleInput): Sample
}
C'est bien pour les petits objets, mais lorsque vous avez beaucoup d'objets avec plus de 10 propriétés dans le schéma, cela deviendra un fardeau.
Le commentaire de Jesse est correct. Pour une réponse plus formelle, voici l'extrait de documentation GraphQL sur les types d'entrées :
Le type d'objet défini ci-dessus est inapproprié pour une réutilisation ici, car les objets peuvent contenir des champs qui expriment des références circulaires ou des références à des interfaces et des unions, dont aucune n'est appropriée pour une utilisation comme argument d'entrée. Pour cette raison, les objets d'entrée ont un type distinct dans le système.
Depuis l'avoir posté, j'ai trouvé que les références circulaires sont en fait acceptables, tant qu'elles sont nulles (sinon cela déclarerait une chaîne infinie). Mais, il existe encore d'autres limitations (par exemple, les interfaces) qui semblent nécessiter un système de type distinct pour les entrées.
De la spécification:
Le type d'objet GraphQL (ObjectTypeDefinition) ... ne convient pas pour la réutilisation [en tant qu'entrée], car les types d'objet peuvent contenir des champs qui définissent des arguments ou contiennent des références aux interfaces et aux unions, aucun des deux ne pouvant être utilisé comme argument d'entrée . Pour cette raison, les objets d'entrée ont un type distinct dans le système.
C'est la "raison officielle", mais il existe plusieurs raisons pratiques pour lesquelles vous ne pouvez pas utiliser un type d'objet comme type d'objet d'entrée ou utiliser un type d'objet comme type d'objet d'entrée:
Les types d'objet et les types d'objet d'entrée ont tous deux des champs, mais ces champs ont des propriétés différentes qui reflètent la façon dont ces types sont utilisés par le schéma. Votre schéma définira potentiellement des arguments et une sorte de fonction de résolution pour les champs d'un type d'objet, mais ces propriétés n'ont aucun sens dans un contexte d'entrée (c'est-à-dire que vous ne pouvez pas résoudre le champ d'un objet d'entrée - il a déjà une valeur explicite). De même, les valeurs par défaut ne peuvent être fournies que pour les champs de type d'objet d'entrée et non pour les champs de type d'objet.
En d'autres termes, cela peut sembler être une duplication:
type Student {
name: String
grade: Grade
}
input StudentInput {
name: String
grade: Grade
}
Mais l'ajout de fonctionnalités spécifiques aux types d'objet ou aux types d'objet d'entrée indique clairement qu'ils se comportent différemment:
type Student {
name(preferred: Boolean): String
grade: Grade
}
input StudentInput {
name: String
grade: Grade = F
}
Les types dans GraphQL sont regroupés en types de sortie et types d'entrée.
Les types de sortie sont des types qui peuvent être renvoyés dans le cadre d'une réponse produite par un service GraphQL. Les types d'entrée sont des types qui sont des entrées valides pour les arguments de champ ou de directive.
Il y a chevauchement entre ces deux groupes (c'est-à-dire les scalaires, les énumérations, les listes et les valeurs non nulles). Cependant, types abstraits comme les unions et les interfaces n'ont pas de sens dans un contexte d'entrée et ne peuvent pas être utilisés comme entrées. La séparation des types d'objet et des types d'objet d'entrée vous permet de vous assurer qu'un type abstrait n'est jamais utilisé là où un type d'entrée est attendu.
Lors de la représentation d'une entité dans votre schéma, il est probable que certaines entités "partageront des champs" entre leurs types d'entrée et de sortie respectifs:
type Student {
firstName: String
lastName: String
grade: Grade
}
input StudentInput {
firstName: String
lastName: String
grade: Grade
}
Cependant, les types d'objets peuvent (et en réalité le font fréquemment) modéliser des structures de données très complexes:
type Student {
fullName: String!
classes: [Class!]!
address: Address!
emergencyContact: Contact
# etc
}
Bien que ces structures peut se traduisent par des entrées appropriées (nous créons un étudiant, nous passons donc également un objet représentant leur adresse), souvent elles ne le font pas - c'est-à-dire que nous devons peut-être spécifier les classes de l'élève par classe ID et ID de section, pas un objet. De même, nous pouvons avoir des champs que nous voulons retourner, mais ne voulons pas muter, ou vice versa (comme un champ password
).
De plus, même pour des entités relativement simples, nous avons souvent des exigences différentes concernant la nullité entre les types d'objet et leurs objets d'entrée "homologues". Souvent, nous voulons garantir qu'un champ sera également retourné dans une réponse, mais nous ne voulons pas faire les mêmes champs requis dans notre entrée. Par exemple,
type Student {
firstName: String!
lastName: String!
}
input StudentInput {
firstName: String
lastName: String
}
Enfin, dans de nombreux schémas, il n'y a souvent pas de mappage un à un entre le type d'objet et le type d'objet d'entrée pour une entité donnée. Un modèle courant consiste à utiliser des types d'objet d'entrée distincts pour différentes opérations pour affiner davantage la validation d'entrée au niveau du schéma:
input CreateUserInput {
firstName: String!
lastName: String!
email: String!
password: String!
}
input UpdateUserInput {
email: String
password: String
}
Tous ces exemples illustrent un point important - alors qu'un type d'objet d'entrée peut refléter un type d'objet de temps en temps, vous êtes beaucoup moins susceptible de le voir dans les schémas de production en raison des besoins de l'entreprise.