As fun as it may be, your GraphQL API is unlikely to be of much use if it doesn’t connect to other systems, be it databases, third-party APIs or alike.
One of the beauties of the architecture imposed by GraphQL (remember all that single responsibility stuff?) is that introducing third-party connectors is both trivial for the developer (you) and completely transparent for the client.
Since resolvers are responsible for fetching the value of a single field, it is easy to imagine that, within a single query response, values could be coming from multiple storages and third-party APIs at the same time, without the client ever being affected.
For this project, you’ll use MongoDB as the persistent storage, but by following the exact same approach you can integrate any other third-party system as the underlying provider for your resolvers.
First off, install MongoDB on your computer
Having that done, add MongoDB Java driver
Thanks to the decision to extract the logic for saving and loading links into the LinkRepository
class, introduction of MongoDB now has a very localized impact.
Update the allLinks method in the Query class to now call linkRepository.getAllLinks()
instead of linkRepository.allLinks()
.
You’ll also have to update GraphQLEndpoint
to connect to MongoDB.
That’s all! Restart Jetty, fire up GraphiQL and give it a spin! Just make sure you create some links before querying them. Everything should still work the same except you won’t lose the saved links if the power goes out.
You may have noticed that the execution strategy seen so far is somewhat naive. Imagine the link descriptions are stored in a different database. That would mean for a query like this
query links {
allLinks {
description
}
}
the resolver for the description
field (invoked once for each link in the result) would query that other database as many times are there were links. This a classic example of the N+1 problem. The solution for this is to batch multiple requests and resolve them in one go. In case of a SQL database, the desired resolver would look like:
SELECT * FROM Descriptions WHERE link_id IN (1,2,3) // fetch descriptions for 3 links at once
DataLoader
In JavaScript and a few other languages, a popular way to implement this strategy is the DataLoader utility. The Java implementation of DataLoader can be found here.
As an alternative, graphql-java
offers BatchedExecutionStrategy
, which looks for resolvers (DataFetcher
s in graphql-java
lingo) annotated by @Batched
. Such resolvers are expected to take a list of source objects and return a list of results. For the example above, that would mean taking a list of links (List<Link>
) and returning a list of descriptions (List<String>
).
Update: graphql-java-tools
now supports batched data fetchers as of this commit!