如何为附加字段组织 GraphQL 解析器
How to organize GraphQL resolver for additional fields
假设我有一个简单的 GraphQL 类型供用户使用:
type User {
id: ID!
name: String!
}
Query {
user(id:ID!)
}
和解析器
user = (_, {id}, {api})=> api.getUser(id)
现在我已经向 User
添加了一个名为 friends 的新字段,并为 User.friends
字段添加了一个新的解析器。
friends = ({id}, _, {api})=> api.getFriends(id)
所以现在我想知道当我们进行这样的查询时,我怎样才能阻止对 api.getUser
的调用但只调用 api.getFriends
.
query {
user(id){
friends {
name
}
}
}
我的理解是,为 Query
类型中的 user
字段定义解析器,它将始终首先调用此解析器,然后再调用 [=14= 中字段的所有解析器]类型。
这是一个常见问题,例如有以下解决方案:https://github.com/gajus/graphql-lazyloader
查看项目的 README 以获取问题的结构化描述。
或者,您可以实现自己的 class,其中包含利用 GraphQL.js 实现默认解析器的方式的缓存值:
class User {
constructor(id) {
this.id = id;
}
getInstance({ api }) {
if (!this.instance) {
this.instance = api.getUser(this.id);
}
return this.instance;
}
// notice how id is already a property of this class
name(args, ctx) {
return this.getInstance(ctx).then(instance => instance.name);
}
// do the same for other fields, user will only be fetched once.
friends(args, { api }) {
return api.getFriends(this.id);
}
}
const resolvers = {
Query: {
user: (args) => new User(args.id),
}
}
如果您使用数据加载器,由于数据加载器中的缓存,您甚至可以用更少的代码完成此操作:
// You probably have this function already somewhere in your apollo server creation
function createContext({ api }) {
return {
api,
loaders: {
user: new Dataloader((ids) => ids.map(id => api.getUser(id))),
},
}
}
const resolvers = {
Query: {
user: (parent, args) => ({ id: args.id }),
},
User: {
name: ({ id }, args, { loaders }) =>
loaders.user.load(id).then(user => user.name),
otherProp: ({ id }, args, { loaders }) =>
loaders.user.load(id).then(user => user.otherProp),
friends: ({ id }, args, { api })=> api.getFriends(id),
}
}
Dataloader 即使被调用两次,也只会到达 API 一次。一个额外的好处是,它将缓存值。理想情况下,您甚至可以在 API 中提供批量加载功能,使加载程序更加高效。
请注意,user.fields.name
现在会向 API 的每个朋友发出呼叫。为避免这种情况,您可以检查 属性 是否存在:
name: (parent, args, { loaders }) =>
parent.name ?? loaders.user.load(parent.id).then(user => user.name),
假设我有一个简单的 GraphQL 类型供用户使用:
type User {
id: ID!
name: String!
}
Query {
user(id:ID!)
}
和解析器
user = (_, {id}, {api})=> api.getUser(id)
现在我已经向 User
添加了一个名为 friends 的新字段,并为 User.friends
字段添加了一个新的解析器。
friends = ({id}, _, {api})=> api.getFriends(id)
所以现在我想知道当我们进行这样的查询时,我怎样才能阻止对 api.getUser
的调用但只调用 api.getFriends
.
query {
user(id){
friends {
name
}
}
}
我的理解是,为 Query
类型中的 user
字段定义解析器,它将始终首先调用此解析器,然后再调用 [=14= 中字段的所有解析器]类型。
这是一个常见问题,例如有以下解决方案:https://github.com/gajus/graphql-lazyloader 查看项目的 README 以获取问题的结构化描述。
或者,您可以实现自己的 class,其中包含利用 GraphQL.js 实现默认解析器的方式的缓存值:
class User {
constructor(id) {
this.id = id;
}
getInstance({ api }) {
if (!this.instance) {
this.instance = api.getUser(this.id);
}
return this.instance;
}
// notice how id is already a property of this class
name(args, ctx) {
return this.getInstance(ctx).then(instance => instance.name);
}
// do the same for other fields, user will only be fetched once.
friends(args, { api }) {
return api.getFriends(this.id);
}
}
const resolvers = {
Query: {
user: (args) => new User(args.id),
}
}
如果您使用数据加载器,由于数据加载器中的缓存,您甚至可以用更少的代码完成此操作:
// You probably have this function already somewhere in your apollo server creation
function createContext({ api }) {
return {
api,
loaders: {
user: new Dataloader((ids) => ids.map(id => api.getUser(id))),
},
}
}
const resolvers = {
Query: {
user: (parent, args) => ({ id: args.id }),
},
User: {
name: ({ id }, args, { loaders }) =>
loaders.user.load(id).then(user => user.name),
otherProp: ({ id }, args, { loaders }) =>
loaders.user.load(id).then(user => user.otherProp),
friends: ({ id }, args, { api })=> api.getFriends(id),
}
}
Dataloader 即使被调用两次,也只会到达 API 一次。一个额外的好处是,它将缓存值。理想情况下,您甚至可以在 API 中提供批量加载功能,使加载程序更加高效。
请注意,user.fields.name
现在会向 API 的每个朋友发出呼叫。为避免这种情况,您可以检查 属性 是否存在:
name: (parent, args, { loaders }) =>
parent.name ?? loaders.user.load(parent.id).then(user => user.name),