Imagine you’re embarking on a new project. You already know that over time, your system and your team will grow—significantly. So, you look for infrastructure that will separate out your concerns, offer flexibility, and allow for quick development.
Sound familiar? This is the situation we at Salto found ourselves in and, for a variety of reasons, we chose this approach: to build our system in a micro services architecture where each service exposes a GraphQL API, and our public API is a single graph of a combination of those services APIs.
Initially developed by Facebook and now managed by a foundation of the same name, GraphQL
offers three primary architectural advantages:
Low maintenance: Flexible queries without the need for new APIs
Low error: The schema enforces type checking
Fast queries: No over-fetching—single queries give you exact data
Yet from day one, we also knew we had to build for scale. We knew the dangers of constructing a monolithic application, and thus one monolithic graph presented a quandary. After considering several options, we selected Apollo Federation
to solve this.
Apollo Federation makes GraphQL lean and manageable. It allows you to divide your graph’s implementation across multiple composable services. The architecture is pretty simple. It contains a single gateway and multiple implementing services where each service implements only the part of the graph it’s responsible for. Therefore, there are no code or schema duplications. Those services then run separately on different servers with their own schema.
Why local federation
While we loved the benefits of Apollo, we didn’t want to begin with remote services from the start. We wanted to grow into it, but weren’t ready for the operational overhead. Local federation allowed us to build that foundation without overburdening our still small team.
Local federation gave us the ‘separation of concerns’ we wanted and allowed us to scale the development process. Now, when we migrate specific services to be remote later on, it’ll be a simple process.
Preparing for local federation required us to separate our code into several small packages, each with a federated schema that it was responsible for. And while a fair amount of work, there’s no calculating the time we saved by not waiting until the code was so large that separating it would be nearly impossible.
So how exactly did we do it?
Let's take a simple example using Apollo Federation. Assume we have three implementing services: User, Post, and Comment. Let's look at their federated type declarations, pictured.
As you can see, an implementing service can reference a type that is declared in another implementation service. In the example, Comment references both User and Post. Comment also extends both User and Post by adding a comments field.
In order to achieve the cross implementation service reference and extension, we needed to add the @key directive as we did in the example. This tells the other services which fields uniquely identify a particular instance of the type.
In addition to the schema, we needed to add a resolver. For example, let's look at an implementation for User resolver:
Now we need to build the gateway and the server:
And that’s basically it.
To summarize: If you are building a new API and you want it to be scalable with micro services you should definitely consider GraphQL, even if you aren’t familiar with it. And if you go this route and evaluate the alternatives, you’ll probably find that Apollo Federation is the way to go. In our example, I demonstrated how you can implement it with Local Federation, without the overhead of managing remote services to begin with, and migrate to remote services only when you need to.
For us, building for scale meant starting with GraphQL and local Apollo Federation.