GraphQL is eating the web apps world right now. Large organisations like Sainsbury’s and Sky are rolling it out into their products. Tech conferences of all languages and disciplines are booking talks on it. Companies like GitHub already have a full GraphQL API alongside their RESTful offering.

However, the majority of the literature available on the subject focuses on how to write front-end code which consumes GraphQL-enabled endpoints.

So what does the backend for a GraphQL-enabled web application actually look like? At Stuart, we’ve been building one with Elixir, and it’s been a delightful experience!

👍 It feels clean and explicit, and our dev team likes it a lot.

🥇 By using a GraphQL schema, the contract between frontend and backend is elevated to a first class citizen.

Let’s dig into how ones goes about building a GraphQL backend using Elixir…

Primer: What is GraphQL?

The above query says:

“I want the zones with a code matching ‘Manchester’, and for you to return the zone code, and the geographical center.”

The shape of the query informs the server on how to build the response content so the client only gets the data that they asked for.

What are the benefits of GraphQL?

  1. Since the client can request only the data it needs, responses are typically smaller because they don’t contain extra data that the client isn’t interested in. Smaller responses = faster responses!
  2. By using a GraphQL schema, the contract between frontend and backend is elevated to a first class citizen. Once a commitment is made to what will be in the schema, it’s easy for frontend developers to ensure they have all the fields they need and it’s easy for backend developers to know which fields they’re expected to be able to respond with.
  3. A great bonus of 3) is that you can run frontend and backend development concurrently by creating a mock schema that returns static data — no need for backend to be a step-ahead in your development pipeline.

Getting Started with Absinthe

So we’re going to use the Absinthe library. This can be setup within a Phoenix app and will provide a lot of useful methods for defining schemas and creating resolver functions that fetch the data.

Setting up Phoenix

mix phx.new graphql_example

then opengraphql_example/lib/graphql_example_web/router.ex and edit as follows:

loading...

This lets Phoenix know that we want Absinthe to handle all API calls for requests that start with /api, and also to set up a testing tool called GraphiQL so we can access its web interface at /graphiql.

Now it’s time to decide what our schema should look like.

The Schema

loading...

The query section at the start defines the expected shape of the query, and the object and field macros help us flesh out the actual contents of the data.

All GraphQL queries must resolve to a set of primitive types. They can bestring, integer, float, boolean, or id. This means all complex objects can eventually be broken down to one of these primitives.

Resolvers

An Absinthe resolver function is passed a function name, some arguments, and a context with more info about the request itself. This is expected to return a two-element tuple where the first element is the atom :ok, indicating success.

This in-turn calls an Ecto module which does the actual database call itself.

loading...

Et voilà! That’s the basic bones of it — now it’s just a case of building up the schema and resolver functions to fetch the data from the relevant tables.

Lessons Learned

  1. It’s non-trivial to write resolvers in such a way that you avoid N+1 queries in the database. Care needs to be taken to aggregate resolver calls in such a way that a single SQL query is issued. This reduces response time and DB load. DataLoader can help a lot with this.
  2. Monitoring performance of your API is totally different now because there’s only one URL for all queries! This means resolvers are themselves the primitive for which monitoring tools must be configured in order to give you a representative picture of performance.

Time to Talk!

Slides are available here! Thanks again to Chris Grice and Sainsbury’s for hosting ❤

Further Reading

Thanks to Stuart Developers. 

0 pouet pouets