GraphQL Overview — Part 2 — Libraries

Piotr Jasiak
SoftwareMill Tech Blog
6 min readFeb 18, 2021

--

Photo by Matthew Henry on Unsplash

This series was co-authored by Kamil Kupczyński, Łukasz Żuchowski, and Sebastian Rabiej.

The second part of the cycle about GraphQL will cover libraries for Scala and JavaScript.

Part 1: What is GraphQL?

Scala libraries

If you want to add GraphQL API to your backend service written in Scala, you will probably face a choice between the 2 most popular libraries — Sangria and Caliban. Let’s take a deeper look into these and try to consider the pros and cons to figure out which could serve you better for your specific needs.

Caliban

The project started by Pierre Ricadat in late 2019, but till the date of writing this article — it’s reached version 0.9.4 and offers many features apart from just the “backend” side. The most important design goals were to deliver a purely functional library with a clean separation of schema definition and implementation. Also, boilerplate code is reduced to the minimum thanks to relying on Magnolia which delivers automatic schema derivation so no need to manually define it. By default, the project uses ZIO as an effects abstraction but no worries — there are interoperability modules for Cats and Monix available.

As mentioned above, the idea is to implement API definitions as simple Scala case classes for (depends on your needs) Queries, Mutations, Subscriptions, and GraphQL schema will be automatically generated during compilation time. Caliban also offers involving some additional processing by offering a Wrapper abstraction. This allows performing actions such as log execution times, add limits for query complexity, support Apollo Tracing, Apollo Caching, and much more. In terms of performance optimization, this library offers tools to reduce duplicated queries or invoke batched queries to the data-source.

Caliban offers a GraphQL client for Scala as an independent project, which delivers a way to interact with such APIs in a type-safe and functional way. It’s worth mentioning that this client uses the sttp library to invoke requests, so you could use any of the sttp supported backends. Additionally, there is an sbt plugin (caliban-codegen-sbt) available, which allows generating Scala clients based on GraphQL schema.

Finally, if your API has to be included as a part of some larger schema, which is called Federation in the GraphQL world (more about Federation), there is also an optional module for that.

Caliban GitHub page.

Sangria

The first release on Github dated July 2015 and it’s still under active development, so it seems to be a more mature library. It uses a different approach to deliver GraphQL API server into the Scala world. Using this library, the starting point will be to define a schema by using GraphQL syntax, which is a general standard. Then, we need to define a Scala model by creating GraphQL types delivered by Sangria. It could sound like a lot of manual work but there is also a set of macros that helps derive definitions based on Scala code. This helps reduce work that needs to be done in cases where we need to repeat some already defined data structures. In terms of serialization library, there are plenty of options including circe or spray-json so you don’t have to stick with one option here. Ok, but how about a server runtime environment? We could serve our API as an akka-http endpoint, play framework endpoint or use a react-relay. Sangria also supports Middleware which could be written as custom operations invoked before/after the query and for the most common cases like logging, tracing, or performance measurements, there are ready solutions available as libraries that could be included in the project, depending on the needs.

Sangria GitHub page.

To summarize the above information, let’s try to compare those libraries:

Category: Schema definition

Caliban: As Scala code, derived at compile-time (magnolia)

Sangria: As a schema file with GraphQL syntax or Scala code

Category: Supported effects libraries

Caliban: ZIO by default with Cats and Monix and interoperability modules

Sangria: Based on Future

Category: Exchangeable serialization mechanism

Caliban: No, mainly uses circe or play-json

Sangria: Yes, circe, play-json, spray-json, argonaut, msgpack, ion, and more

Category: Subscriptions support

Caliban: Yes, supports only ZStream

Sangria: Yes, provided sangria-streaming-api with akka-streams, RxScala, and Monix ready to use implementations

Category: Supported http-servers

Caliban: http4, akka-http, play, finch

Sangria: http4s, akka-http, play, relay

Category: Additional tools (clients, generators)

Caliban: Scala client, sbt plugin for client code generation, schema generation, middleware

Sangria: Middleware supported and many ready to use solutions available as libraries

JavaScript frontend libraries — Apollo Client

GraphQL clients are powerful tools that help with GraphQL API integration on the frontend. You may wonder — why do I even need a library if GraphQL requests are just plain HTTP ones? Why not use XHR, fetch, Axios, or anything else you’re already familiar with?

Well, as usual, libs come with many benefits. Let’s have a quick look at one of them — Apollo Client. It is one of the most popular libraries (with others being Facebook-based Relay and urql) that comes with a lot of features. It’s also very powerful, but you don’t need to use every feature from the very beginning, which makes it easy to introduce into any project.

Firstly, Apollo Client integrates with pretty much every well-known UI framework. This includes React, Vue, Svelte, Ember, and Angular. In the case of React, you get hooks and higher-order components for data fetching. Whether it is a query or mutation, Apollo will not only take care of sending a request but also expose flags tracking its state (Is it done loading? Are there any errors?) and will update the view once the state changes. This also reduces boilerplate and helps write more declarative code.

In addition, once the request is done, you have a number of additional tools that let you interact with it:

  • Apollo provides the refetch function that you can use to repeat the query, with different variables if needed (particularly useful when working with pagination, filtering, etc.)
  • You can set pollInterval to instruct Apollo to refetch data and update components at specified intervals;
  • If there’s a need to perform additional work when data arrives or an error occurs, you can register custom callback functions to do that;
  • Deferring queries until some condition is met is also possible with the use of useLazyQuery hook or skip option;
  • In case of a mutation, you can provide a response that will be used before the actual result comes back from the server. This approach is called Optimistic UI.

Furthermore, Apollo uses a concept called links to let you easily compose actions around data handling. What does it mean? Basically, every action performed in Apollo is being ‘passed through’ one or more links that may interact with requests, e.g., add headers or set custom variables to the request, log operation time, etc.

One of the biggest drawbacks of using GraphQL over REST is its inability to cache responses from the server on the HTTP level. There’s no resource identifier, and browsers cannot distinguish whether subsequent requests are asking for the same data. Apollo helps with that, too! In its basic form, Apollo cache keeps all server responses on the front-end in a normalized form during the application lifetime. This way, the client will respond to future queries without even sending a network request. Of course, this behavior is not always desirable, as you expect your data to change over time. That’s why the cache is highly configurable — you may directly state that you want your data to be fetched over the network instead of from the local memory, you may modify cache contents after performing mutation, etc. And if you want the cache to persist between application runs — you can always store its data in a number of ways.

In the third and last episode, we will talk about caching, load balancing, API gateways, and we’ll summarize the whole series.

GraphQL Overview — Part 3 — The Infrastructure and Summary

--

--