我正在使用graphql-tools@v6
,并且已经实现了两个指令@map
和@filter
。我的目标是像地图和过滤器管道一样使用它们。在某些情况下,我想在过滤之前进行映射,在其他情况下,反之亦然。指令是使用Schema Directives API实现的,并且仅应用一个指令时它们就会按预期工作。
但是,如果我一起使用它们,那么它们总是以一种特定的顺序执行,这与在模式中声明它们的方式不匹配。
例如
directive @map on FIELD_DEFINITION
directive @filter on FIELD_DEFINITION
# usage
type MyType {
list1: [String!]! @map @filter
list2: [String!]! @filter @map
}
在这种情况下,两个字段都被映射然后过滤,反之亦然。顺序由我如何在schemaTransforms
属性中传递它们来控制。
const schema = makeExecutableSchema({
schemaTransforms: [mapDirective, filterDirective] # vs [filterDirective, mapDirective]
});
我相信,因为这些转换是作为数组传递的,所以它们的执行顺序取决于数组的顺序。我可以用directiveResolvers
代替它们,但是它们的作用有限。
但是让我失望的是文档中的以下声明
使用directiveResolvers的现有代码可以考虑迁移以直接使用mapSchema
因为它们在执行顺序方面有不同的行为,所以我看不到它们如何互换。
有人可以解释是否有一种方法可以确保模式指令按照它们在特定字段的模式中使用的顺序执行?
请参阅this github issue for in depth discussion。
新API与directiveResolvers
或schemaDirectives
的工作方式不同。 schemaTransform
在下一个模式之前应用于整个模式,这与其他两个模式相反,在前两个模式中,所有变换都在访问下一个字段节点之前应用于特定的字段。我认为有两种解决方法:
创建一个新的@pipeline
指令,该指令将获取其他指令的名称列表,然后以类似directiveResolvers
的顺序应用它们。
我走了一些不同的路线,在其中我创建了一个新函数attachSchemaTransforms
,就像attachDirectiveResolvers
一样,它访问每个节点并按顺序应用所有指令。
export function attachSchemaTransforms(
schema: GraphQLSchema,
schemaTransforms: Record<string, FieldDirectiveConfig>, // a custom config object which contains the transform and the directive name
): GraphQLSchema {
if (typeof schemaTransforms !== 'object') {
throw new Error(`Expected schemaTransforms to be of type object, got ${typeof schemaTransforms}`);
}
if (Array.isArray(schemaTransforms)) {
throw new Error('Expected schemaTransforms to be of type object, got Array');
}
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: oldFieldConfig => {
const fieldConfig = { ...oldFieldConfig };
const directives = getDirectives(schema, fieldConfig);
Object.keys(directives).forEach(directiveName => {
const config = schemaTransforms[directiveName];
if (config) {
const { apply, name } = config;
const directives = getDirectives(schema, fieldConfig);
if (directives[name]) {
const directiveArgs: unknown = directives[name]
apply(fieldConfig, directiveArgs);
return fieldConfig;
}
}
});
return fieldConfig;
},
});
}