What are the ramifications of differing entity definitions?

I’m working to migrate from one system to another, and as such my entities have two primary keys, one is a reference to the new system, and the second is a reference to the old system.

type User @key(fields: "oldId") @key(fields: "newId") {
  oldId: ID!
  newId: ID!
}

Some of our subgraphs are able to resolve based on only the old id, others off of only the new id, and others off of either. I was under the impression that the entity definition had to match across all subgraphs, but I’ve seen now that that is not the case. Subgraphs can disagree about which fields are keys.

So my question is: what are the ramifications of subgraphs disagreeing? Will I run into problems if one subgraph declares @key(fields: "oldId") and another doesn’t declare that key? Can a subgraph resolve out entities with a different subgraphs key if it doesn’t know that it considers that a key?

For instance if I declare

type User @key(fields: "oldId") {
  oldId: ID!
  newId: ID!
}

and then resolve

 return {
   __typename: "User",
  newId: "SomeUUID"
 } 

Is that legal? Will the next subgraph in the query plan be able to resolve off of that if it declares newId as a key? Should I always resolve out the id that I know about?

What happens if I query for a field that @requires(fields: "oldId") but the initial subgraph didn’t resolve it? Will it hit some other subgraph to find it?

Hello :wave:
As long as there exists some execution path to resolve all the requested data, it is perfectly legal to have different entity @key definitions across subgraphs. @key information is used by the router/gateway to come up with an optimal query plan on how to fetch all the requested data.

i.e. given subgraph A

type Query {
  foo(id: ID!): Foo
}

type Foo @key(fields: "id") {
  id: ID!
  a: String
  # some other fields here but no "oldId"
}

and a subgraph B

type Foo @key(fields: "oldId") {
  oldId: ID!
  b: String
  # some other fields here but no "id"
}

It is not possible to resolve the following query as there is no way to reach b field with only information from the A subgraph

{
  foo(id: "123") {
    a
    b
  }
}

We can make it valid if we add another subgraph C that defines both keys (so the query plan would result in calls to A → C → B).

type Foo @key(fields: "id") @key(fields: "oldId") {
  id: ID!
  oldId: ID!
  c: String
  # other fields
}

I’d recommend to run some of the test queries against your supergraph and check out the generated query plans to see exactly what is happening there. See query plan docs for more details.

1 Like