DEV Community

Héctor Valls
Héctor Valls

Posted on

GraphQL for server-side resource aggregation

When implementing service oriented and microservices architectures, sooner or later, the aggregation of data coming from different services becomes a problem to face. In this post, we will see how GraphQL can help with this.

(A basic knowledge of GraphQL is required. You can learn GraphQL basics on the official website).

We are developing a property rental API, and the consumer needs an endpoint to fetch information about a property and its landlord. An example response would look like this:

GET /properties/1234

{
    id: 1234,
    address: "10 Downing Street",
    area: 90,
    landlord: {
        id: 882,
        name: "John Doe"
        email: "john.doe@mail.com"
    }
}

We can see two aggregates involved in this piece of information: Property and Landlord.

In a monolithic application, we would write a query, with some SQL JOIN maybe, to fetch the data from a database, and marshall it to the above JSON.

However, let's assume in our rental organization we have a microservices architecture; we have a Property Microservice and Landlord Microservice, with a database for each one. For that reason, we can't just JOIN two database tables; integration between services is made across the network and not through database sharing.

We need an API Gateway or BFF to aggregate data from both services and serve it to the API consumer.

We could perform aggregation "by hand":

//This code belongs to API Gateway / BFF
fun getProperty(id: Int) : JsonObject {
    val property = propertyService.getById(id)
    val landlord = landlordService.getById(property.landlordId)
    return buildJson(property, landlord)
}

Note that property object, fetched from propertyService::getById, does not contain information about its landlord, but only its identifier; landlordId. We use that field to fetch the whole landlord info, through landlordService, and construct the full response JsonObject.

When implementing this approach, due to its stream-based nature, reactive programming tools like ReactiveX are quite useful.

An alternative to this, is letting GraphQL engine do the job.

Resource aggregation using GraphQL

First, we are going to define the GraphQL schema:

type Query {
  getProperty(id: String!): Property
}

type Property {
  id: String
  address: String
  area: Int
  landlord: Landlord
}

type Landlord {
  id: String
  name: String
  email: String
}

Now, we have to define a data fetcher for getProperty operation:

fun getPropertyDataFetcher() : DataFetcher {
  return DataFetcher { env -> {
     val propertyId = env.getArgument("id")
     return propertyService.getProperty(propertyId)
  }
}

So far, we have the information about the property. Now, we need to implement the data fetcher for its landlord:

fun getLandlordDataFetcher() : DataFetcher {
  return DataFetcher { env -> {
     val property = env.getSource<Property>()
     val landlordId = property.landlordId
     return landlordService.getLandlord(landlordId)
  }
}

With the schema and the fetchers, we are ready to build our GraphQL instance and make the query:

val graphQL = buildGraphQLFromSchemaAndFetchers()
executeQuery(graphQL, """{ query:
  getProperty(id: 1234) {
    id
    address
    area
    landlord {
      id
      name
      email
    }
  }
}""")

With this approach, we delegate the logic of the data composition to GraphQL library implementation, this way being less error-prone than doing it manually.

It looks like a declarative way; you tell it how to fetch properties, and how to fetch landlords. Then, just ask for the data you need and data aggregation is managed by GraphQL.


Sometimes, we tend to think of GraphQL just as a type of HTTP API; an alternative to REST.
This is true, but using GraphQL does not mean starting a GraphQL server. You don't need to expose an API of this type at all, depending on the use case you are trying to solve.

In this example, I have shown how to use GraphQL as a query engine and data aggregator, but I haven't mentioned anything about GraphQL servers.

Let's consider this code, that could be written using some modern REST tool:

@GET
fun getProperty(@PathParam("id") id: String) : Property {
  val property = executeQuery(graphQL, """{ query:
    getProperty(id: $id) {
      id
      address
      area
      landlord {
        id
        name
        email
      }
    }
  }""")
  return property
}

This is a REST API resource, but, under-the-hood, a GraphQL engine is responsible for aggregation of data from several sources. There is no need to run any GraphQL server.

There are many reasons you may not want to expose a pure GraphQL API. e.g. hard integration with frontend or other services, maintain compatibility, lack of GraphQL knowledge amongst the developers, or even organizational convention. Anyway, you can take advantage of GraphQL power and use it as a query engine, like we have just seen above.

I hope you enjoyed the article!

Oldest comments (2)

Collapse
 
ben profile image
Ben Halpern

Nice post

Collapse
 
hhccvvmm profile image
Héctor Valls

Thank you!