Why Exposing Foreign Keys in GraphQL is Bad
There are many pitfalls in GraphQL development, and I believe exposing Foreign Keys is one. We have to start this topic with what a foreign key is and give some background on how foreign keys commonly are used in non-GraphQL environments.
Foreign Keys
A foreign key is a field where an entity's primary key is referenced from within another entity. An example of a foreign key could be an Article
having an authored_by
field referencing a User
by its primary key. The use of foreign key describes relations between entities to answer questions like "which articles have a specific user authored".
Usage in Non-GraphQL Environments
It is common to request data based on the entitys primary key in REST
and REST-like
endpoints towards a specific URL, e.g. /users/[user_id]
or /articles/[article_id]
.
The data returned to get information about a specific User
could look something like this:
{
id: "user_id",
name: "some name",
authored_articles: ["article_id1", "article_id2"]
}
Moreover, it could look like this for an Article
:
{
id: "article_id",
title: "some title",
authored_by: "user_id"
}
Suppose these are the endpoints we have available, and the user_id
is the only thing known. In that case, it requires us to start fetching the User
information through /users/[user_id]
and then, per each article_id
in authored_articles
, fetch information about said Article
through /articles/[article_id]
. After fetching this information, we can list the titles of all Articles
the User
has authored.
Optimizations of cases like this are possible. We could create a specific endpoint only returning the titles a User
has authored. The endpoint could be defined as /authored_titles/[user_id]
, which could return data as follows:
{
titles: ["some title", "some other title"]
}
Developing and maintaining a large set of endpoints is not an easy task. Changes to client behaviour can require much rework to keep it optimized. This is where GraphQL comes with all its upsides.
GraphQL
There are many advantages to using GraphQL. One of them is solving the issues described in the "Usage in Non-GraphQL Environments" section above. A well-implemented GraphQL server should make it easy to fetch all the needed information. I believe the GraphQL query we should be able to ask our GraphQL server looks like this.
query User_Articles($userId: ID!) {
user(id: $userId) {
id
articles {
id
title
}
}
}
This results in data returned looking like the following:
{
data: {
user {
id: "user_id",
articles: [
{
id: "article_id1",
title: "some title"
},
{
id: "article_id2",
title: "some other title"
}
]
}
}
}
The issue here is if we instead would design the schema to use foreign keys to reference Articles
inside a User
. That would put us in the same situation as the REST
and REST-like
environments and prevent us from querying all the data we need in one query.
Conclusion
To benefit from one of GraphQLs most significant advantages, which is to fetch exactly the data needed in one query, we need to stop exposing foreign keys and instead expose the modelled GraphQL type for the foreign entity.
