Motivaton
Extend support for Apollo Federation according to the federation specs
User Impact
Users will now be able to use the Apollo federation server which provides a unified interface for querying any combination of GraphQL backend services.
Implementation
Introduction
According to the federation docs, the architecture of federation consists of 2 components:-
1- Collection of GraphQL servers also known as implementing services
.
2 - A Gateway
that essentially constructs a federated Graph
from the schemas of those distinct implementing services
.
Gateway is basically a query planner that breaks a GraphQL query and fetch data from different implementing services according to where the data is stored.
Implementation
In order to extend support for federation, we need to implement the federation schema specification and generate resolver for entities
.
We need to implement the following additions to the schema:
scalar _Any
scalar _FieldSet
# a union of all types that use the @key directive
union _Entity
type _Service {
sdl: String
}
extend type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
directive @external on FIELD_DEFINITION
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(fields: _FieldSet!) on OBJECT | INTERFACE
directive @extends on OBJECT | INTERFACE
We will not be extending support for @provides and @requires directive initially.
What is an Entity?
According to the federation docs, an entity
is a type that we define in one implementing service and extend it in other services. These are the core building blocks of the federated graph
.
It is denoted by adding a @key
directive to the definition.
for eg:
type Product @key(fields: "upc") {
upc: String!
name: String!
price: Int
}
The field which is given as an argument to the @key directive must be a primary key. In our case, it should be of the ID
type or have an @id
directive.
Note
: Federation specs allow defining multiple primary keys
or compound primary keys
but we will not implement it now for the sake of simplicity.
Type Extension
Since federation encourages the principle of separation of concern for the schema designing which allows us to define one type
in one implementing service
and extend
it in other services
.
Suppose for the given schema:
type User {
name: String
recentPurchases: [Product]
reviews: [Review]
}
type Product {
name: String
price: String
reviews: [Review]
}
type Review {
body: String
author: User
product: Product
}
If we were to break it into 3 implementing services concerning User
, Product
, and Review
.
It will be as follows:
Service1:
type User {
name: String
}
Service2:
type Product {
name: String
price: String
}
extend type User {
recentPurchases: [Product]
}
Service3:
type Review {
body: String
author: User
product: Product
}
extend type User {
reviews: [Review]
}
extend type Product {
reviews: [Review]
}
We will allow type extension with both extend
keyword and @extends` directive. Then it will look something like this:
type User @extends{
recentPurchases: [Product]
}
or
extend type User {
recentPurchases: [Product]
}
Entity Resolvers:
For an implementing service to reference a type that is defined in some other implementing service, we need entity resolvers
which takes __typename
and primary key
of that type and returns a unique object. It will closely follow the definition given here
References:
http://discuss.dgraph.io/t/graphql-apollo-federation-demo/5130/