Using Bindings with Fragments for a Stitched Schema
See original GitHub issueMy questions are how to use fragments with bindings and how to use Dataloader with bindings.
To preface, I may be completely lost in my own head on this problem. So let me give the 100,000ft view and hopefully there’s an answer.
I have two servers, one for vehicles and one for files. The vehicles server needs to stitch in the file server. The vehicle server wants to query:
{
vehicles {
year
make
....
heroImages {
url
}
}
heroImages
comes from the file server. Here’s how I’ve implemented this so far.
I used get-schema
to save a copy of the file server to the vehicle server. I imported the needed types through schema.graphql
.
My app schema:
const appSchema = makeExecutableSchema({
typeDefs: [
importSchema(`./src/schema.graphql`),
`extend type Vehicle { heroImages: [FileGroup!]! }`
],
resolvers
})
Then I build the binding for the file server
const makeFileServiceLink = new HttpLink({
uri: `url`,
fetch
})
const remoteSchema = makeRemoteExecutableSchema({
schema: await introspectSchema(makeFileServiceLink),
link: makeFileServiceLink
})
const fileServer = new Binding({
schema: remoteSchema,
fragmentReplacements: {}
})
All good so far. But here’s where things go sideways. Obviously heroImages
is an N+1 call. So I made a Dataloader…
const heroImagesLoader = new DataLoader(async ids => {
const images = await fileServer.query.vehicleHeroImages(
{ input: { vehicleIds: ids } },
)
return ids.map(id => {
const FileGroup = images.find(({ groupId }) => groupId === id)
return FileGroup ? [FileGroup] : []
})
})
And the resolver…
{
Vehicle: {
heroImages: {
fragment: `fragment VehicleFrag on Vehicle { stockNo }`,
resolve: async ({ stockNo }, source, ctx, info) => {
const { heroImagesLoader } = ctx.loaders
return heroImagesLoader.load(stockNo)
}
}
}
}
The first few problems rear their heads.
-
How to get
info
into the binding query. DataLoader’s API is pretty strict. I’ve made it work by turning it into an object, but whoa hack…heroImagesLoader.load({ id: stockNo, info})
and then mapping the ids and passingids[0].info
for theinfo
binding parameter. -
How to get a field into the binding if it’s not queried. In my set up, the vehicle server stores a
stockNo
for each vehicle. The file server groups files by a groupId. For vehicles, the stockNo is the groupId. Therefore, I cannot map the results back without knowing the groupId field. I’m sure there’s a hack I could do toinfo
, but isn’t there a better way?
I tried messing with the binding’s fragmentReplacement
to no avail. e.g.
const fragmentReplacements = extractFragmentReplacements({
Query: {
vehicleHeroImages: {
fragment: `fragment GroupId on FileGroup { groupId }`
}
}
})
const imageServer = new Binding({
schema: remoteSchema,
fragmentReplacements
})
to no avail.
Any help is a huge help! Thanks everyone as always for your time and effort!
Issue Analytics
- State:
- Created 5 years ago
- Comments:10 (4 by maintainers)
Thanks a lot for your detailed description, @LawJolla.
I am curious to hear the perspective of @kbrandwijk, @schickling and @freiksenet on this topic 🙂
Thanks @schickling !
Here’s the minimal repo. (I kept my development / shared endpoints in the code to make it faster on everyone)
https://github.com/LawJolla/graphql-stitched-bindings-example