GraphQL

GraphQL is a query language for APIs. It provides an approach to developing web APIs and has been compared and contrasted with REST and other web service architectures. It allows clients to define the structure of the data required, and the same structure of the data is returned from the server, therefore preventing excessively large amounts of data from being returned. GraphQL is a specification, not an implementation, this means that GraphQL isn’t actually a technology but a set of rules and concepts.

Lets do some research regarding data over-fetching and under-fetching in relation to the REST Architecture. How can both of these issues be addressed using GraphQL?

GraphQL Resources:

Exercise 1

In this part of the onboarding the idea is to replicate the same reality than in the previous exercise, but this time, instead of having a REST API built with Node.js and Express, we will be building a GraphQL API making use of Node.js and Apollo Server.

Here there are some resources that might be interesting to skim through in order to learn about GraphQL and Apollo Server:

In order to complete this part of the onboarding, the following must be done:

  • Define types and create resolvers for them, so a client querying your API will be able to execute the following queries:
    • fetch a user/tweet/comment given its id
    • fetch all users/tweets/comments
    • create a user/tweet/comment
    • update a user/tweet/comment
    • delete a user/tweet/comment

Take into account that tweets, users and comments are not isolated concepts, so again, relationships have to be defined between them. As an example, the types should be defined in such a way that your server understands that a user has many comments, or that a tweet belongs to a user.

If all of these relationships are implemented correctly, once finished this exercise, the GraphQL API must be capable of resolving the following query:

query {
  comments {
    tweet {
      text
      user {
        name
      }
    }
  }
}

Exercise 2 - N + 1 Problem

In order for the problem we will be discussing in the next section to became obvious, it'll be nice to have more than 6 users on the database. So first, lets add more users. Once this is done, lets execute the following query and watch the logs of the server.

query {
  users {
    tweets {
      text
    }
  }
}

How many queries to the database are being made? Definitely too many. This is called the N+1 problem.

Opening/Closing database connections is an expensive process, so it doesn't seem to be appropriate to open so many connections to a database to fulfill simple queries. Therefore, in this part of the onboarding we will be researching about the N+1 problem and finding a way to solve it in the context of GraphQL.

Once finished one should be able to answer these four questions:

  • What do we mean when we say N+1?
  • Why does this problem occur?
  • Which is most accepted conceptual strategy to solve it?
  • Which utility do we use to solve it in the context of GraphQL and Node.js?

After all the reasearch made, one should already have found a tool to solve the problem that can be applied to the project. So now it's time to incorporate this utility into the project we are building. Once incorporated, the system should be able to resolve the GraphQL query presented above in exactly 2 queries to the database.

GraphQL Federation

TIME TO SCALE THINGS UP!

Monolithic systems, as the one we've been building, are difficult to manage when the system itself or the number of team members working at it starts to grow. So, a common practice once a system scales up is to migrate it to what we call a micro service architecture. Basically, migrating to a micro service architecture consists in dividing our entire system into small pieces (services), so each service is now in charge of a different thing. The main benefits of migrating to a micro service architecture are the following:

  1. Improved Scalability: The capacity of each microservice to run autonomously makes it relatively easy to add, remove, update, and scale individual microservices. This can be done without disrupting the other microservices that compound the application. Also, when demand increases, you only need to upgrade or divert more resources to the microservice affected by the increasing demands.

  2. More Resilient Applications: With a microservices architecture, the failure of one service is less likely to negatively impact other parts of the application because each microservice runs autonomously from the others.

  3. Programming Language and Technology Agnostic: When creating a microservices-based application, developers can connect microservices programmed in any language. They can also connect microservices running on any platform. This offers more flexibility to use the programming languages and technologies that best fit the needs of each of the services and the teams involved.

  4. Faster Time to Market: The pluggability of a microservices application architecture allows for easier and faster application development and upgrades. Developers can quickly build or change a microservice, then plug it into the architecture. Moreover, due to the independence of each microservice, teams don't have to worry about coding conflicts, and they don't have to wait for slower-moving projects before launching their part of the application.

As everything in life, it has its disadvantages too:

  1. You will add communication between parts of your system (services), that will need to communicate between them via an API. This adds a point of failure.

  2. Your teams start to loose knowledge of the system as a whole, they only know specific parts of it.

So, as we've seen, dividing our system into different services is a great choice when things start to scale. However, clients that consume APIs don't want to interact with different services depending on the task they want to fulfil, or to interact with multiple services and then gather the information themselves. Instead, it's much more simple for a client to interact with a unique interface. The interface would be responsible to interact with each of the services, gather the data that comes from them and then finally respond to the client. This means that the interaction between the exposed interface and each of the services completely transparent to the client consuming the API.

A common approach that allow clients of APIs to interact with a unique interface, is to create what we call a gateway. A gateway stands in front of all the services, and its responsible for exposing them as a hole, so clients can interact with the system through a unique interface.

In our case, since we are building a GraphQL API, this gateway should help us access all of our system's data by typing a single GraphQL query, even if that data lived in separate places. To make this migration to a micro service architecture we will be using Apollo Federation. An Apollo Federation architecture consists of:

  • A collection of subgraphs (usually represented by different back-end services) that each define a distinct GraphQL schema
  • A gateway that uses a supergraph schema (composed from all subgraph schemas) to execute queries across multiple subgraphs

Apollo Federation is often described as a way to scale your GraphQL APIs, and a big part of this is team management. The federated architecture allows teams in charge of different parts of the schema (different services) to focus on their own types without being distracted by details of the domain that aren't pertinent to them.

Here you have some interesting videos you might want to check before moving to the next exercise:

Exercise 3 - Federated Architecture

Next we'll be migrating our project to a micro service architecture making use of Apollo Federation. In order to complete this excercise, you have to do the following:

  • Implement the following 3 services: a users service, a tweets service, and a comments service. Each of the services should now be in charge of a different part of our GraphQL schema and must have its own database. Also, each of the services must have their own package.json.
  • Implement a gateway so clients can consume our API through it, without interacting with each of the services directly.

In preparation for this exercise, first go to the Apollo Federation documentation and take a look at the Separation of Concerns section, this should help you decide the following things:

  1. How to divide your schema into different pieces.
  2. Which of the services should be in charge of each of those pieces.

Resources: