Summary
In GraphQL Apollo managed federation, I'm trying to figure out if it's possible for one service to have an aggregate field, that returns an array of entities two separate services. As in, I'm trying to support this use case:
query FromTwoServices {
aggregateField {
id
...on Service1Entity {
field1
}
...on Service2Entity {
field2
}
}
}
where both
type Service1Entity implements Aggregation
and
type Service2Entity implements Aggregation
And under the hood, the resolver for aggregateField
(which lives on Service 2) calls down down to Service 1 to Service1Entity
s. So this field is aggregating two data sets.
Example Problem
In my current GraphQL setup, I have all of this defined in one service:
interface Animal {
id: ID!
name: String!
}
type Cat implements Animal {
id: ID!
name: String!
meows: Boolean
}
type Dog implements Animal {
id: ID!
name: String!
barks: Boolean
}
When I query my service, it hits an animals
resolver, and looks like:
query Animals {
animals {
id
...on Cat {
meows
}
...on Dog {
barks
}
}
}
Now I'm creating a Dogs
service. I would like Dogs
to come from the Dog service, and Cats to come from the Cats service. Additionally, I would still like to support my animals
aggregation field, and I'd like to move it to the Dogs
service.
To support this, I've made Cat
an entity, and duplicated the interface across both services. To allow Dog service to resolve the Cat
type, I've used "referencing" to tell Dog service that Cat
lives somewhere else.
Cat Service (marking Cat as entity with @key
):
interface Animal {
id: ID!
name: String!
}
type Cat implements Animal @key(fields: "id") {
id: ID!
name: String!
meows: Boolean
}
Dog Service:
interface Animal {
id: ID!
name: String!
}
type Cat implements Animal @key(fields: "id") {
id: ID! @external
}
type Dog implements Animal {
id: ID!
name: String!
barks: Boolean
}
The problem with this setup is the Dog service fails to start, because of this GraphQL error:
Error: Interface field Animal.name expected but Cat does not provide it.
In my Dog service, if I add an external name
field, like so:
type Cat implements Animal @key(fields: "id") {
id: ID! @external
name: String! @external
}
Then the two services fail to compose in managed federation:
This data graph is missing a valid configuration. [dog] Cat.name -> is marked as @external but is not used by a @requires, @key, or @provides directive.
I sort of feel like I'm going down the wrong path here. Is it possible to have an aggregate field like this, where one service can return the its own entities, combined with the entities of another service?
We already have lots of clients querying for the animals
field, so we're trying to support this change without requiring client changes, aka without restructuring the use of an interface.