@inaccessible Keys in Federated GraphQL APIs - A Deep Dive - WunderGraph
TL;DR
The @inaccessible directive hides fields from the public client schema while keeping them available in the Supergraph for the Router to use. Applied to @key fields, this unlocks two patterns: Internal Keys — join subgraphs using different internal identifiers without exposing them to clients, useful for heterogeneous databases and legacy migrations — and Zero Trust Data Access — pass short-lived access tokens between services as inaccessible keys, so sensitive data requires explicit authorization even from internal services.
Using @inaccessible keys in your Subgraphs is a very powerful addition to your federated GraphQL Schema design toolbox. In this post, we'll be looking at two very powerful patterns that you can implement using @inaccessible keys.
| Pattern | Problem it solves |
|---|---|
| Internal Keys | Different internal IDs across subgraphs/databases |
| Legacy Migration | Supporting old and new identifiers during migration |
| Zero Trust Access | Enforcing strong auth for sensitive data across services |
- Internal Keys - How to hide data access keys from the public
- Zero Trust Data Access - How to manage access to sensitive data in a federated GraphQL API
The basics of the @key directive in GraphQL Federation
What does the @key directive do in GraphQL Federation?
The @key directive defines a unique identifier for a type in your federated GraphQL schema. This identifier can be used by the Router to "join" data from different subgraphs together.
Here's a simple example of how we could use the @key directive to join Posts from a Blog subgraph with Authors from a User subgraph:
# Blog subgraph
type Post @key(fields: "id") {
id: ID!
title: String!
author: User!
}
type User @key(fields: "id") {
id: ID!
posts: [Post!]!
}
# User subgraph
type User @key(fields: "id") {
id: ID!
name: String!
}
type Query {
user(id: ID!): User
}
How does the @inaccessible directive work in federated GraphQL APIs?
To understand the @inaccessible directive we have to first understand how GraphQL Federation Routers work, and how they expose data from different subgraphs to the public.
The Router serves one schema which is publicly accessible (client schema) and another which is a superset (Supergraph schema). The Supergraph schema can contain @inaccessible fields that are not part of the public schema, but are still usable internally for the Router's operations.
Using the @inaccessible directive for Internal Keys in Federated GraphQL APIs
When should I use internal @inaccessible keys instead of public IDs?
This is important in API design as it helps to hide implementation details from the public. For instance, if the User subgraph uses a serial primary key in a PostgreSQL database, while a Post subgraph uses UUIDs, we can leverage the @inaccessible directive to add an internal key in the User type.
Using @inaccessible keys to migrate legacy systems in Federated GraphQL APIs
You can support both legacy and new identifiers during a migration. For example, if we want to migrate the User subgraph from MySQL to PostgreSQL, we can add an @inaccessible key for the new UUID identifier while retaining the old serial key until the migration is complete.
Using the @inaccessible directive for Zero Trust Data Access in Federated GraphQL APIs
In a Zero Trust architecture, we enforce that sensitive data is only accessible if the client has valid credentials. For example, a Patient service could expose a shortLivedAccessToken as an @inaccessible field, which cannot be accessed publicly but can be used internally to control access to other sensitive data.
Conclusion
In this exploration, we've discussed how the @inaccessible directive transforms schema design in federated GraphQL APIs. The patterns we examined, including Internal Keys and Zero Trust Data Access, provide powerful techniques for managing identifiers across different subgraphs and securing access to sensitive data.