I’m seeking some clarification regarding the use of the @key directive in Apollo Federation. I understand that @key functions as a primary key to efficiently query entities within a service. However, I have a specific scenario and a few questions related to it.
Consider the following schema:
type User @key(fields: "email") {
email: String!
id: ID!
name: String!
}
In this schema, I’ve defined the User type with the @key directive specifying the “email” field as the unique key. Now, suppose I have two users with the same email address because it is not mentioned as unique in my service, for example, “abc@xyz.com.”
My use case involves combining this User data with another service, let’s say “Product.” For each email, I intend to fetch the product details.
My questions are:
What happens if two users with the same email, like “abc@xyz.com,” exist in the User service? How does Apollo Federation handle this situation when querying by email?
Does the presence of the @key directive affect the outcome in this scenario? If so, how?
I appreciate any insights or guidance you can provide on this matter. Thank you!
Hello @keys should specify a set of fields that uniquely identify the entity across of the subgraphs. If your @key is not unique then you can end up with incorrect data depending on which subgraph gets resolved first.
@keys don’t have to reference single field → they can reference any valid selection set of fields on a given entity (e.g. in your example instead of email maybe you should be using id OR a combination of email and id to uniquely identify entity?). Different subgraphs can even have different @keys. See our docs for details.
Thanks for the response @dkuc . I wanted to know what exactly will happen when I use @key and still violate the uniqueness rule.
Lets say my data has which is marked by @key User contains
Federated supergraph is not a database so router cannot enforce uniqueness of the underlying data as it is unaware of it at all. It is an implementation detail of your subgraph to guarantee that entities can be uniquely identified.
As for what happens → query plan will be generated for your specified query (which I believe is invalid). Assuming you actually want
# subgraph User
type User @key(fields: "email") {
email: String!
name: String
}
type Query {
user(email: String!): User
}
# subgraph Product
type Product @key(fields: "productID") {
productID: ID!
description: String
}
type User @key(fields: "email") {
email: String!
products: [Product!] # <---- adding new field to user
}
Router will first attempt to resolve user query to get user and based on the result it will then attempt to resolve the User entity in the product subgraph. You can see the resulting query plan in the Apollo Studio. If your Users and Products subgraphs are non-deterministic and returns you data either for user foo or bar for given email then your results will be non-deterministic either. It is an implementation detail of your subgraphs how this data gets resolved so whatever logic you got there will dictate what data you will get.
Again, if your current @key does not guarantee uniqueness you should update it to make it unique. Otherwise you may end up with some inconsistent data depending on which query plan is created and how subgraphs resolve those keys.