I'm working on a GraphQL project where I have multiple schemas, and one of the schemas includes a Union type (Site) which can represent different types, such as PhysicalSite and WaitingRoom.
I need to use batchDelegateToSchema to efficiently resolve the 'sites' field on the 'Study' type, which returns a SitesConnection that includes the Site Union type.
My current setup involves multiple subschemas, and I'm using batchDelegateToSchema to delegate parts of the query to the relevant subschemas. However, I'm facing challenges when working with the Union type within batchDelegateToSchema.
Example of the batchDelegateToSchema I'm using now:
Candidate: {
site: {
selectionSet: '{ siteId, studyId }',
resolve(candidate, args, context, info) {
return batchDelegateToSchema({
schema: study,
operation: 'query',
fieldName: 'sites',
key: { siteId: candidate.siteId, studyId: candidate.studyId },
transforms: [transformSites(info)],
argsFromKeys: keys => ({
filter: {
studyIds: keys
.map(key => key.studyId)
.filter(Boolean),
siteIds: keys.map(key => key.siteId).filter(Boolean),
},
}),
valuesFromResults: (results, keys) => {
keys.map(findByStudyAndSiteId(results));
},
context,
info,
});
},
},
I was trying to implement a custom transform but I got an error when I try to use the query and the reason is that I have nodes from the connection and cannot find the nested values
"message": "No type was found for field node { kind: \"Field\", name: { kind: \"Name\", value: \"id\" } }.",
query candidate($id: ID!) {
candidate(id: $id) {
siteId
name
site {
... on PhysicalSite {
id
}
}
}
}
The transform flow:
const extendedSelection =
(...fields) =>
(query = {}) => {
console.log('FFF', JSON.stringify(fields, null, 2));
console.log('QQQ', JSON.stringify(query, null, 2));
return {
...query,
selections: [
...(query.selections ?? []),
...fields.map(value => ({
kind: Kind.FIELD,
name: { kind: Kind.NAME, value },
})),
],
};
};
const extendedInlineFragmentNodesSelection = (subtypeName, ...fields) => {
const extendedSelectionSet = extendedSelection(...fields);
return (query = {}) => {
const result = {
kind: Kind.SELECTION_SET,
selections: [
{
kind: Kind.INLINE_FRAGMENT,
typeCondition: {
kind: Kind.NAMED_TYPE,
name: {
kind: Kind.NAME,
value: subtypeName,
},
},
selectionSet: extendedSelectionSet(query),
},
{
kind: Kind.FIELD,
name: {
kind: Kind.NAME,
value: 'nodes',
},
selectionSet: extendedSelectionSet(query),
},
],
};
return result;
};
};
export const transformSites = info => {
let typeName = '';
info.fieldNodes[0].selectionSet.selections.forEach(selection => {
if (
selection.kind === 'InlineFragment' &&
(selection.typeCondition.name.value === 'PhysicalSite' ||
selection.typeCondition.name.value === 'WaitingRoom')
) {
typeName = selection.typeCondition.name.value;
}
});
return new WrapQuery(
['sites'],
extendedInlineFragmentNodesSelection(typeName, 'id', 'studyId'),
result => {
return result;
},
);
};
How can I use batchDelegateToSchema to resolve a field that returns a Union type?
How do I handle resolving Union type fields in different subschemas using batchDelegateToSchema?
Is it possible to merge the results of batchDelegateToSchema calls for a Union type like Site into a single Union type?
I'd appreciate any guidance or examples on how to efficiently use batchDelegateToSchema with schemas that contain a Union type like Site. Any code examples or best practices would be highly valuable.
Thank you in advance for your help!
My codes as a reference define the union in this way in the schema
union Site = PhysicalSite | WaitingRoom
type SitesConnection {
nodes: [Site!]!
totalCount: Int!
}
extend type Study {
sites(
siteIds: [ID!] # e.g. SWE-1 for PhysicalSite, WR:7 for WaitingRoom
physicalSitesFilter: SiteFilterForPhysicalSites! = { include: true }
waitingRoomsFilter: SiteFilterForWaitingRooms! = { include: true }
): SitesConnection!
}
input SiteFilterForPhysicalSites {
include: Boolean! = true
ids: [ID!]
statuses: [PhysicalSiteStatus!]
statusNot: PhysicalSiteStatus
}
input SiteFilterForWaitingRooms {
include: Boolean! = true
ids: [ID!]
}
type Query {
sites(
filter: SiteFilter
pagination: Pagination! = { limit: 1000, skip: 0 }
): SitesConnection!
}
The resolver
export default {
Query: {
sites: (_, { filter, pagination }) => ({
...filter,
...pagination,
}),
},
SitesConnection: {
totalCount: ({ physicalSitesFilter, ...filter }, _, ctx) =>
SiteService(ctx).countSites({ ...filter, ...physicalSitesFilter }),
nodes: ({ physicalSitesFilter, ...filter }, _, ctx) => {
console.log('Resolving SitesConnection nodes');
return SiteService(ctx).getSites({ ...filter, ...physicalSitesFilter });
},
},
};