Share dataloader between loadable objectFields #1307
-
So far I've not found a way to avoid making duplicate db queries when adding aliases/conveniences fields that depend on another field. E.g. query MyQuery {
viewer {
email
memberships {
edges {
node {
org {
name
}
}
}
}
# orgs is basically just an alias/convenience field based on memberships
orgs {
edges {
node {
name
}
}
}
}
} // ...
builder.objectField(User, 'memberships', (t) =>
t.loadable({
type: UserOrgMembersConnection,
byPath: true,
args: {
...t.arg.connectionArgs(),
},
resolve: (member) => member.id,
load: async (ids: Array<string>, ctx, args) => {
const orgMembersById = await ctx.findOrgMembersByUserIds(ids)
return ids.map((id) =>
resolveArrayConnection({ args }, orgMembersById[id] ?? []),
)
},
}),
)
builder.objectField(User, 'orgs', (t) =>
t.loadable({
type: UserOrgsConnection,
byPath: true,
args: {
...t.arg.connectionArgs(),
},
resolve: (user) => user.id,
load: async (ids: Array<string>, ctx, args) => {
// Here I'd like to ideally call the dataloader on User.memberships but AFAICT there is no way to get it?
// e.g. User.memberships.getDataloader(ctx).loadMany(ids)
const orgMembersById = await ctx.findOrgMembersByUserIds(ids)
return ids.map((id) =>
resolveArrayConnection(
{ args },
orgMembersById[id].map((member) => member.orgId) ?? [],
),
)
},
}),
) Is this possible today and I've just missed it, or is this something unsupported that I need to create an own dataloader for? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Creating your own data-loader for this is the right way to go. I've looked into trying to support this in the data-loader plugin, but couldn't find an API that seemed better than just creating the dataloader directly. One thing that might be useful is the const getMemberLoader = createContextCache(() => new DataLoader((ids: readonly string[]) => {
// Implement dataloader here
}))
builder.objectField(User, 'memberships', (t) =>
t.loadable({
type: UserOrgMembersConnection,
byPath: true,
args: {
...t.arg.connectionArgs(),
},
resolve: (member) => member.id,
load: async (ids: Array<string>, ctx, args) => {
const orgMembersById = await getMemberLoader(ctx).loadMany(ids)
return ids.map((id) =>
resolveArrayConnection({ args }, orgMembersById[id] ?? []),
)
},
}),
) |
Beta Was this translation helpful? Give feedback.
Creating your own data-loader for this is the right way to go. I've looked into trying to support this in the data-loader plugin, but couldn't find an API that seemed better than just creating the dataloader directly.
One thing that might be useful is the
createContextCache
method, that lets you get a dataloader without having to add it to the context object directly. Not really different than what you are already doing, but I like it for encapsulation