De la especificación:
El tipo de objeto GraphQL (ObjectTypeDefinition) ... no es apropiado para su reutilización [como entrada], porque los tipos de objeto pueden contener campos que definen argumentos o contener referencias a interfaces y uniones, ninguno de los cuales es apropiado para su uso como argumento de entrada . Por esta razón, los objetos de entrada tienen un tipo separado en el sistema.
Esa es la "razón oficial", pero hay varias razones prácticas por las que no puede usar un tipo de objeto como tipo de objeto de entrada o usar un tipo de objeto como tipo de objeto de entrada:
Funcionalidad
Los tipos de objetos y los tipos de objetos de entrada tienen campos; sin embargo, esos campos tienen propiedades diferentes que reflejan cómo el esquema usa estos tipos. Su esquema definirá potencialmente argumentos y algún tipo de función de resolución para los campos de un tipo de objeto, pero estas propiedades no tienen sentido en un contexto de entrada (es decir, no puede resolver el campo de un objeto de entrada, ya tiene un valor explícito) . Del mismo modo, los valores predeterminados solo se pueden proporcionar para campos de tipo de objeto de entrada y no para campos de tipo de objeto.
En otras palabras, esto puede parecer una duplicación:
type Student {
name: String
grade: Grade
}
input StudentInput {
name: String
grade: Grade
}
Pero agregar características específicas para tipos de objetos o tipos de objetos de entrada deja en claro que se comportan de manera diferente:
type Student {
name(preferred: Boolean): String
grade: Grade
}
input StudentInput {
name: String
grade: Grade = F
}
Limitaciones del sistema de tipos
Los tipos en GraphQL se agrupan en tipos de salida y tipos de entrada .
Los tipos de salida son tipos que pueden devolverse como parte de una respuesta producida por un servicio GraphQL. Los tipos de entrada son tipos que son entradas válidas para argumentos de campo o de directiva.
Hay superposición entre estos dos grupos (es decir, escalares, enumeraciones, listas y no nulos). Sin embargo, los tipos abstractos como uniones e interfaces no tienen sentido en un contexto de entrada y no se pueden usar como entradas. Separar los tipos de objetos y los tipos de objetos de entrada le permite asegurarse de que nunca se utilice un tipo abstracto donde se espera un tipo de entrada.
Diseño de esquemas
Al representar una entidad en su esquema, es probable que algunas entidades "compartan campos" entre sus respectivos tipos de entrada y salida:
type Student {
firstName: String
lastName: String
grade: Grade
}
input StudentInput {
firstName: String
lastName: String
grade: Grade
}
Sin embargo, los tipos de objetos pueden (y en realidad lo hacen con frecuencia) modelar estructuras de datos muy complejas:
type Student {
fullName: String!
classes: [Class!]!
address: Address!
emergencyContact: Contact
# etc
}
Si bien estas estructuras pueden traducirse en entradas apropiadas (creamos un Estudiante, por lo que también pasamos un objeto que representa su dirección), a menudo no lo hacen, es decir, tal vez necesitemos especificar las clases del estudiante por ID de clase e ID de sección, no un objeto. De manera similar, es posible que tengamos campos que queramos devolver, pero que no queremos mutar, o viceversa (como un password
campo).
Además, incluso para entidades relativamente simples, a menudo tenemos diferentes requisitos en torno a la nulabilidad entre los tipos de objetos y sus objetos de entrada "contrapartes". A menudo queremos garantizar que un campo también se devuelva en una respuesta, pero no queremos que los mismos campos sean obligatorios en nuestra entrada. Por ejemplo,
type Student {
firstName: String!
lastName: String!
}
input StudentInput {
firstName: String
lastName: String
}
Por último, en muchos esquemas, a menudo no existe un mapeo uno a uno entre el tipo de objeto y el tipo de objeto de entrada para una entidad determinada. Un patrón común es utilizar tipos de objetos de entrada separados para diferentes operaciones para ajustar aún más la validación de entrada a nivel de esquema:
input CreateUserInput {
firstName: String!
lastName: String!
email: String!
password: String!
}
input UpdateUserInput {
email: String
password: String
}
Todos estos ejemplos ilustran un punto importante: si bien un tipo de objeto de entrada puede reflejar un tipo de objeto en algunas ocasiones, es mucho menos probable que lo vea en los esquemas de producción debido a los requisitos comerciales.