我们的情况是GraphQL Query的响应必须返回对象的一些动态属性。在我们的例子中,我们无法预定义所有可能的属性 - 因此它必须是动态的。
我们认为有两种方法可以解决它。
const MyType = new GraphQLObjectType({
name: 'SomeType',
fields: {
name: {
type: GraphQLString,
},
elements: {
/*
THIS is our special field which needs to return a dynamic object
*/
},
// ...
},
});
正如您在示例代码中看到的那样,元素是必须返回对象的属性。解决此问题时的响应可能是:
{
name: 'some name',
elements: {
an_unkonwn_key: {
some_nested_field: {
some_other: true,
},
},
another_unknown_prop: 'foo',
},
}
1)返回"任意对象"
我们可以返回任何对象 - 因此GraphQL不需要知道Object有哪些字段。当我们告诉GraphQL该字段是GraphQlObjectType类型时,它需要定义字段。因此,似乎无法告诉GraphQL某人只是一个对象。
我们已经改变了这个:
elements: {
type: new GraphQLObjectType({ name: 'elements' });
},
2)我们可以定义动态字段属性,因为它在函数
中当我们将字段定义为函数时,我们可以动态定义对象。但是字段函数需要一些信息(在我们的例子中是传递给元素的信息),我们需要访问它们来构建字段对象。
示例:
const MyType = new GraphQLObjectType({
name: 'SomeType',
fields: {
name: {
type: GraphQLString,
},
elements: {
type: new GraphQLObjectType({
name: 'elements',
fields: (argsFromElements) => {
// here we can now access keys from "args"
const fields = {};
argsFromElements.keys.forEach((key) => {
// some logic here ..
fields[someGeneratedProperty] = someGeneratedGraphQLType;
});
return fields;
},
}),
args: {
keys: {
type: new GraphQLList(GraphQLString),
},
},
},
// ...
},
});
这可行,但问题是如果有办法将args和/或解析对象传递给字段。
问题 所以现在我们的问题是:在我们的GraphQL案例中推荐哪种方式,解决方案1或2可能吗?也许有另一种解决方案?
修改 使用ScalarType时,解决方案1将起作用。例如:
type: new GraphQLScalarType({
name: 'elements',
serialize(value) {
return value;
},
}),
我不确定这是否是推荐的方法来解决我们的情况。
答案 0 :(得分:11)
这两种选择都不可行:
GraphQL是强类型的。 GraphQL.js不支持某种any
字段,并且模式中定义的所有类型都必须定义字段。 If you look in the docs,fields
是必需的 - 如果您尝试将其删除,则会发生错误。
Args用于基于每个请求解析查询。您无法将它们传递回您的架构。您的架构应该是静态的。
正如您所建议的那样,通过滚动您自己的客户Scalar可以实现您尝试做的事情。我认为更简单的解决方案是使用JSON - 您可以导入custom scalar for it like this one。然后让您的elements
字段解析为包含动态字段的JSON对象或数组。如果需要,您还可以根据参数操作解析器内的JSON对象(例如,如果要将返回的字段限制为args中定义的子集)。
警告信息:使用JSON或包含嵌套数据的任何自定义标量的问题在于,您要限制客户端在请求实际需要的内容时的灵活性。它还会导致客户端的错误更少 - 我更倾向于告诉我请求的字段不存在或在我发出请求时返回null 而不是稍后发现我得到的JSON blob并没有包括我期望它的字段。
答案 1 :(得分:0)
另一种可能的解决方案是将任何这样的动态对象声明为字符串。然后从解析器函数将对象的字符串化版本作为值传递给该对象。然后最终您可以再次将该字符串解析为JSON,以使其再次成为客户端上的对象。
我不确定是否建议使用该方法,但我尝试使其与这种方法一起使用,并且确实运行良好,因此在此分享。