I have RefType and it's widely using in other types:
import {
GraphQLObjectType,
GraphQLString,
} from 'graphql';
const RefType = new GraphQLObjectType({
name: 'Ref',
fields: {
i: { type: GraphQLString, description: 'ObjectId as string' },
t: { type: GraphQLString, description: 'ORM-model class name' },
},
});
export default RefType;
Today I want to pass this type like input field for mutation:
const NotesAddMutation = mutationWithClientMutationId({
name: 'NotesAdd',
inputFields: {
ref: { type: RefType },
text: { type: GraphQLString },
},
outputFields: {
...
And get error NotesAddInput.ref field type must be Input Type but got: Ref. A couple minutes of googling gets answer to use GraphQLInputObjectType.
So I have several questions:
1) Does GQ have some object type for my RefType, which I can use for queries and mutations? My RefType is like scalar type, but have two values. Analogous type may be LatitudeLongitudeType as example.
2) What is proper way convert GraphQLObjectType to GraphQLInputObjectType? Or I should define two types (one type for queries, second for mutations and may be third in future, when subscriptions come)?
3) May be error message can be improved, because right now it does not help to determine what is wrong?
PS. If somebody tries to find understanding of a design purpose GraphQLObjectType and GraphQLInputObjectType, you may read https://medium.com/@HurricaneJames/graphql-mutations-fb3ad5ae73c4#.wi9iwkgot
I'll answer 1 and 3 first.
InputObjectType can only have other input types as it's members. This is because ObjectType might have mutually-recursive dependencies with other types, which can't be resolved to a JSON structure. Thus you can't use Ref directly in InputObjects. Error is just telling you that - ObjectType is not a kind of type you can use as input and thus as part of input object.
Now for question number 2. There are multiple approaches to how to handle this, in some APIs, you might design all your inputs to be custom and thus never convert from object types. Otherwise we've followed the following approach at Reindex:
ObjectType (through type.getFields())InputObjectType as isObjectTypeObjectType has Relay's Node interface), then add reference to it (id) to InputObjectInputObject and add itGraphQLNonNull or GraphQLList, inspect the inner type as above, and add result wrapped in corresponding wrapperOne thing to note - non-nullity has a bit different meaning in GraphQLObject and GraphQLInputObject. For input object it means that the field is required, for objects that means that not returning a non-null value for that field is an error. You might, in some cases, have non-null in InputObject, but not in an Object, because null is often a valid return value in GraphQL, eg when permissions are invalid.
Here is example code that implements the above algorithm:
import { mapValues } from 'lodash';
import {
GraphQLNonNull,
GraphQLScalarType,
GraphQLInputObjectType,
GraphQLEnumType,
} from 'graphql';
export default function createInputObject(
type
) {
return new GraphQLInputObjectType({
name: type.name + 'Input',
fields: mapValues(type.getFields(), (field) =>
convertInputObjectField(field)
),
});
}
function convertInputObjectField(
field,
) {
let fieldType = field.type;
const wrappers = [];
while (fieldType.ofType) {
wrappers.unshift(fieldType.constructor);
fieldType = fieldType.ofType;
}
if (!(fieldType instanceof GraphQLInputObjectType ||
fieldType instanceof GraphQLScalarType ||
fieldType instanceof GraphQLEnumType)) {
fieldType = fieldType.getInterfaces().includes(NodeInterface) ?
ID :
createInputObject(fieldType)
}
fieldType = wrappers.reduce((type, Wrapper) => {
return new Wrapper(type);
}, fieldType);
return { type: fieldType };
}
@freiksenet
YAY, many thanks!!!
Definitely, +100 to karma for next brewing ;)
@freiksenet Can you explain where NodeInterface is defined? I scanned the entire GraphQL source (0.8.1) and can't find it.
Also, did you mean GraphQLID rather than ID? If not, where is ID defined?
@johanatan you may use this ready solution:
import { TypeComposer } from 'graphql-compose';
const yourConvertedInputType = TypeComposer.create(YourOutputType).getInputType();
Most helpful comment
@johanatan you may use this ready solution: