如何在传递给解析器之前修改父节点?
How to modify parent node before it is passed to a resolver?
假设我们有这个 GraphQL 模式:
type Venue implements Node {
country: Country!
id: ID!
name: String!
nid: String!
url: String!
}
此解析器支持:
// @flow
import type {
VenueRecordType,
ResolverType,
} from '../types';
const Venue: ResolverType<VenueRecordType> = {
country: (node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
},
};
export default Venue;
我希望能够在解析器字段使用父节点/节点参数值之前对其进行修改。
据我阅读文档所知,实现此目的的唯一方法是实现和包装每个字段,例如
// @flow
import type {
VenueRecordType,
ResolverType,
} from '../types';
const createNodeDecorator = (fieldResolver) => {
const updateNode = (node) => {
// https://media0.giphy.com/media/12NUbkX6p4xOO4/giphy.gif
return node;
};
return (parent, parameteres, context, info) => {
return fieldResolver(updateNode(parent), parameteres, context, info);
};
};
const Venue: ResolverType<VenueRecordType> = {
country: createNodeDecorator((node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
}),
id: createNodeDecorator((node) => {
return node.id;
}),
name: createNodeDecorator((node) => {
return node.name;
}),
nid: createNodeDecorator((node) => {
return node.nid;
}),
url: createNodeDecorator((node) => {
return node.url;
}),
};
export default Venue;
有没有更好的方法?
理想情况下,我会在使用解析器之前调用一个 __load
挂钩,例如
const Venue: ResolverType<VenueRecordType> = {
__load: (parent, parameteres, context, info, next) => {
next(parent, parameteres, context, info);
},
country: (node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
},
};
但这(据我所知)并不存在。
如何在传递给解析器之前修改父节点?
实现此目的的一种方法是将修改节点的逻辑向上移动一个级别。例如,给定如下查询类型:
type Query {
venues: [Venue!]!
}
我们可以在解析器中执行以下操作:
const resolvers: {
Query: {
venues: async (root, args, context) => {
const venues = await context.loaders.VenueLoader.load()
return venues.map(magic)
}
}
}
这行得通,但这意味着您必须在 returns 一个地点或地点列表的任何解析器中复制逻辑,这是乏味且容易出错的。如果您已经在使用加载器,我会把这个逻辑移到加载器本身中,然后就此结束。
但是,我们可以更进一步,也可以使用架构指令。例如,如果您想为不同的类型重用相同的逻辑,或者出于某些奇怪的原因,您只想修改某些字段的父级,这将很有帮助。下面是一个示例,可让您将指令应用于类型或单个字段:
class MagicDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = function (source, args, context, info) {
return resolve.apply(this, [magic(source), args, context, info])
}
}
visitObject(object) {
const fieldMap = object.getFields()
for (const fieldName in fieldMap) {
this.visitFieldDefinition(fieldMap[fieldName])
}
}
}
然后只需将指令作为 schemaDirectives
的一部分传递给您的 ApolloServer
配置,并将其包含在您的类型定义中:
directive @magic on FIELD_DEFINITION | OBJECT
假设我们有这个 GraphQL 模式:
type Venue implements Node {
country: Country!
id: ID!
name: String!
nid: String!
url: String!
}
此解析器支持:
// @flow
import type {
VenueRecordType,
ResolverType,
} from '../types';
const Venue: ResolverType<VenueRecordType> = {
country: (node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
},
};
export default Venue;
我希望能够在解析器字段使用父节点/节点参数值之前对其进行修改。
据我阅读文档所知,实现此目的的唯一方法是实现和包装每个字段,例如
// @flow
import type {
VenueRecordType,
ResolverType,
} from '../types';
const createNodeDecorator = (fieldResolver) => {
const updateNode = (node) => {
// https://media0.giphy.com/media/12NUbkX6p4xOO4/giphy.gif
return node;
};
return (parent, parameteres, context, info) => {
return fieldResolver(updateNode(parent), parameteres, context, info);
};
};
const Venue: ResolverType<VenueRecordType> = {
country: createNodeDecorator((node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
}),
id: createNodeDecorator((node) => {
return node.id;
}),
name: createNodeDecorator((node) => {
return node.name;
}),
nid: createNodeDecorator((node) => {
return node.nid;
}),
url: createNodeDecorator((node) => {
return node.url;
}),
};
export default Venue;
有没有更好的方法?
理想情况下,我会在使用解析器之前调用一个 __load
挂钩,例如
const Venue: ResolverType<VenueRecordType> = {
__load: (parent, parameteres, context, info, next) => {
next(parent, parameteres, context, info);
},
country: (node, parameters, context) => {
return context.loaders.CountryByIdLoader.load(node.countryId);
},
};
但这(据我所知)并不存在。
如何在传递给解析器之前修改父节点?
实现此目的的一种方法是将修改节点的逻辑向上移动一个级别。例如,给定如下查询类型:
type Query {
venues: [Venue!]!
}
我们可以在解析器中执行以下操作:
const resolvers: {
Query: {
venues: async (root, args, context) => {
const venues = await context.loaders.VenueLoader.load()
return venues.map(magic)
}
}
}
这行得通,但这意味着您必须在 returns 一个地点或地点列表的任何解析器中复制逻辑,这是乏味且容易出错的。如果您已经在使用加载器,我会把这个逻辑移到加载器本身中,然后就此结束。
但是,我们可以更进一步,也可以使用架构指令。例如,如果您想为不同的类型重用相同的逻辑,或者出于某些奇怪的原因,您只想修改某些字段的父级,这将很有帮助。下面是一个示例,可让您将指令应用于类型或单个字段:
class MagicDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = function (source, args, context, info) {
return resolve.apply(this, [magic(source), args, context, info])
}
}
visitObject(object) {
const fieldMap = object.getFields()
for (const fieldName in fieldMap) {
this.visitFieldDefinition(fieldMap[fieldName])
}
}
}
然后只需将指令作为 schemaDirectives
的一部分传递给您的 ApolloServer
配置,并将其包含在您的类型定义中:
directive @magic on FIELD_DEFINITION | OBJECT