Apollo Federation, TypeGraphQL, and Reference Resolvers
Enable GraphQL reference resolvers with Apollo Federation and TypeGraphQL Framework
Sunday, Sep 5, 2021
After spending several frustrating hours trying to get reference resolvers to work with Apollo Federation & TypegraphQL, decided to write up how to get it setup.
Full Github Project Here
Apollo Federation Recap
Apollo Federation gives us the ability to decouple monolithic services with large schemas and graphs into smaller subgraphs. Enabling to break up your architecture into individual microservices.
To communicate with all the subgraphs and schemas, a single gateway (known as the "Gateway") sits in front and acts as a single point of entry for frontend applications.
This architecture improves security by the gateway being the only microservice needing to be publicly accessible to the internet. Just have to make sure to configure the appropriate firewall rules and network access control lists to allow the Gateway to access all of the subgraphs.
TypegraphQL
What is it?
TypegraphQL is a modern Typescript framework where you can build GraphQL APIs in Node.js with classes and decorators
Reference Resolvers and their use case
With having a federated graph, you might need to access related entities that are housed in another subgraph. Not to be confused with fetching unrelated entities from 1 API call and resolving after the request has been made.
Such as
Common example would be resolving "Users" from multiple subgraphs.
Imagine you have an individual subgraph microservice that you store your User table and information in.
Then in another microservice, say "Posts", you store the user id
of the person who created the post, along with other post information.
First hunch when formulating a query strategy would be to grab posts and users, then resolve the information in the frontend to match the appropriate user information by its id.
Separate
However, with the help of Reference Resolvers, a query like this is possible instead.
Referenced
Resolving User subgraph data from the Post subgraph
We'll be enabling the graphql query above with the help of TypegraphQL and Apollo Federation.
First in the User subgraph, need to first update your TypeGraphQL decorated User entity.
Need to add the @Directive decorator passing in the @key directive, specifying the fields used as the primary key used to uniquely identify and fetch the User object. In this case, the unique identifier would be the id
.
user/src/User/User.ts
This should generate the following SDL in the User graph
Next, we'll have to create a reference resolver for the User entity in order for the gateway to know how to resolve the entire User object.
users/src/User/user-reference.ts
Here in the resolveUserReference function, we're telling apollo federation that we can resolve the entire User entity, we just expect as input a incomplete User type with just an id
property. (Our other microservices can store this id value in their databases and supply this partial User object)
Last step in our User microservice, pass resolveUserReference, into our buildFederatedSchema function.
Now that our User entity is ready to be resolved from another microservice, we need to update the example Post subgraph.
post/src/User/User.ts
Here we add a few decorators to the User Model in the post microservice. Just like in the User microservice, we have the @key
directive along with an additional @extends
directive.
This @extends
directive lets the gateway know that we wish to extend the original subgraph where the User is from.
Finally, we have the @external
Directive, decorated over the id
Field. This indicates to the gateway that the field originates in another subgraph.
post/src/Post/Post.ts
Now in the Post entity file, notice we have a @Field
decorator resolving a type of User, and we're returning an object with a singular property of id
, set by the stored createdById field.
We use TypeGraphQL's @Root
directive to be able to reference other fields on this object in the createdByUser function.
This createdByUser object will then get passed into the resolveUserReference function we created prior in the external User subgraph.
Now that has all been set up, we can resolve the entire User type from our Post query through the gateway server.
query
response
Now from a single fetch of a Post, we have the related createdByUser information from our User subgraph. No need to do any mapping client side after the graphql request has been resolved.
Resolving Users created posts from the Post subgraph
Now since we established that the User type is extended, external, with the @key directive specifying the unique identifies, we can do the opposite.
We can also grab all posts that users have created, to make this query possible
In the Post microservice, we'll add a new Resolver, that resolves to a User type.
post/src/User/resolver.ts
In this UserPostsResolver, we have a @FieldResolver
that returns an array of Posts, and generates the following SDL
Now that apollo gateway server knows how to resolve fetching posts created by users, we can complete the query shown above.
query
response
Final Thoughts
Even though it takes a lot of boilerplate code to get this working with TypeGraphQL, I'm still a fan of this library.
TypeGraphQL has an Apollo Federation example, have it linked below and highly recommend to go check out. In the example it wasn't exactly clear to me how to resolve data that wasn't in one microservice.
If you had trouble trying to get reference resolvers working as well hope this helps.
Happy Coding!