I have a subgraph A which defines an interface X and several types X1, X2, … Xn that implement it.
I want to define a type T in another subgraph B that looks like this:
type T {
x: X!
...
}
I don’t want subgraph B to need to know what possible types implement X (so for example, I don’t want to declare a union type X1 | X2 | … | Xn for T.x in B).
How can I declare and reference X in B, and resolve references to it in A to achieve this? Ideally I’d like to do something as simple as returning { '__typename': 'X', 'id': <id> }
in my resolver for T.x, then leave it up to A’s reference resolver to deal with mapping that to an actual type instance
But all of the federation schema composition examples I’ve come across so far are based on types rather than interfaces, so I don’t believe this approach will work.
This is a tough one. Because all subgraphs are valid GraphQL APIs, it’s not possible for a subgraph to use an interface name for __typename like this:
// this is invalid!
{
"data": {
"t": {
"x": {
"__typename": "X", // interface name
"id": "123"
}
}
}
}
In the current version of federation, our hands are tied. Can you invert the relationship between subgraphs and have the subgraph that knows about the concrete types of X
provide this field? This should be possible if T
is an entity:
interface X {
id: ID!
}
type A implements X {
id: ID!
}
extend type T @key(fields: "id") {
id: ID! @external
x: X
}
There’s some discussion around allowing subgraphs to treat an interface as an object type, and teaching the query planner about jumping from an “interface object” to a concrete type in other subgraphs. But it’s a difficult problem to optimize so we don’t have a timeline for this feature.
Hope this helps!
Thanks for your reply @lennyburdette - unfortunately the nature of subgraphs A and B in my example means that its not really possible for A to provide the field (think of this as something like subgraph A defines interface X=Animal implemented by X1=Dog, X2=Cat, etc., and subgraph B defines type T=Person having property x=pet - it wouldn’t make sense for A to concern itself with knowing about people, or by extension any other concept where there is a relationship to an animal).
It feels to me that this is likely to be quite a common pattern in monolithic graphs, and that the current { __typename: <type>, id: <id> }
“stub pattern” is unsuitable in this case, not only because (as you rightly point out) you can’t assign an interface name to __typename
, but also because I suspect its quite common that subgraph B isn’t going to know what the actual type is (nor should it have to, because it’s better that B isn’t coupled to A in that way - I want to be able to add new implementations of X to A without the need to change B).
In essence what I need is for B to be able to return the pseudo-reference { _interfacename: 'X', id: <id> }
, and for A to be able to declare a reference resolver on the interface X that, given an id, can return the appropiate concrete instance of some type implementing X.
Agreed! I actually filed the oldest issue about this topic: federation: queries for interface types require knowledge of concrete implementations · Issue #336 · apollographql/federation · GitHub
Keep an eye on the Apollo blog for news about Federation 2. Again, I don’t know the timeline on addressing this concern but we’re definitely working on it.
Thanks for the issue pointer; I’ve added my 2 cents to the discussion on that because this seems to me like a pretty significant feature for graph decomposition.