in Elixir, GraphQL, Phoenix

Phoenix GraphQL Tutorial with Absinthe

Phoenix GraphQL with Absinthe

This post is a tutorial on using Phoenix and GraphQL to build a clean and powerful API with Elixir. We will start with a fresh Phoenix application and walk through turning it into a GraphQL server step-by-step.

Initial Setup

I recommend following along with a fresh Phoenix install and will assume that you’ve already run mix phoenix.new  and have an app ready to go. I’m going to use Ecto with Postgres in this post but you are welcome to use any database you’d like. You’ll just need to modify the resolver code we write later on in order to return data from your particular data source.

We are going to need a few different libraries to get everything setup so let’s go ahead and add them now. Add the following libraries to your deps and list of applications:

After updating your  mix.exs file run  mix deps.get to pull in all the new libraries.

This is a quick summary of what we just added:

Absinthe

The  absinthe ,  absinthe_plug , and  absinthe_ecto libraries are the specific GraphQL libraries that I’ve chosen to work with. There are a couple Elixir GraphQL libraries but I’ve found these to be the best and easiest to work with. We’ve also added  poison because it is required by  absinthe_plug .

Faker

Faker is a nice library for creating fake data to populate our database with.

Adding Users, Posts, and Some Data

Before we dive into GraphQL we’ll need to ensure that our data source has some example data for us to play with. First, let’s add a user model and run the migration that was generated:

Second, add a post model and run the migration:

I’ve chosen this blog-like data model in order to demonstrate using GraphQL to query  data that has associations.

Our next step is to make a simple change to the user  model in order to take advantage of Ecto’s association querying. Open up your user  model and add the following line:

Let’s also generate some seed data that we can query later on. Add this code to your priv/repo/seeds.exs  file:

Run mix run priv/repo/seeds.exs  to fill your database with some data and we should be good to go.

GraphQL Types

GraphQL Types are similar to a database schema. However, GraphQL Types do not need to represent the actual structure of the data in your database. They are extremely flexible and allow you to do things like creating data structures that represent data from a number of data sources or even virtual data fields that are temporary like a login token. The GraphQL spec is pretty easy to read so if you’d like to know more about types head on over to the type system description.

Let’s start building out the GraphQL Types for our user and post data. Create a new file at  web/schema/types.ex and add the following code:

At the top of the file we pull in the  Absinthe.Schema.Notation module which provides us with a DSL for defining our GraphQL Types. We also use  Absinthe.Ecto to enable helpers for batching association requests. The next bit of code defines our types. As you can see, we define both a user  and post  type. Each of these are represented as a GraphQL Object. Here is a quick definition from the spec:

GraphQL Objects represent a list of named fields, each of which yield a value of a specific type.

A few things to note here are that the  id fields have a special type of  id  which represents a unique identifier. This can be used for things like caching and other fun stuff. The other fields have the scalar type :string . There are a number of predefined scalar types and you can also add your own. So, for instance, you could create a :url  scalar type that ensures that a particular field is a valid URL. You’ll also notice the use of the  resolve  and  assoc helpers on the user.posts  field and the post.user  field. These are really slick utility functions from the  Absinthe.Ecto library that allow us to batch requests in order to avoid N+1 queries.

With that we’ve got our types all setup. Let’s move on to building out our schema.

GraphQL Schemas

The GraphQL Schema is where we define queries that a client can access. Each query is represented as a field and you describe how that query will get its data in the form of a resolver function. Let’s dive into our schema to see what this looks like. Add a file at web/schema.ex  and add the following code:

At the top we pull in Absinthe.Schema  which provides us with a DSL for building out our queries. We also use the import_types  helper to pull in the types that we previously defined. This utility function is very useful for breaking up your code into manageable pieces. In a real application your types are likely going to be huge. You can separate each of these types into their own files and then pull them into your schema with the import_types  helper.

The main portion of this file is the query definition. We define fields for :posts  and :users  and then pass our MyApp.PostResolver.all/2  and MyApp.UserResolver.all/2  functions to the resolve  helper. These two fields are similar to an index function within a phoenix controller in that they return a list of a particular resource. Within each of these resolver functions we will describe how to get our data. Let’s build those next.

GraphQL Resolvers

In GraphQL, resolvers describe how to get data for a particular query. This is where you can use a library like Ecto to get data or, in the case of a database unsupported by Ecto, you can use a database client directly to return data. Let’s create two new files at web/resolvers/post_resolver.ex  and web/resolvers/user_resolver.ex  and add the following code:

Repo.all  is going to return a list of Ecto structs for each resource but the important thing to note is that you just need to return a list of maps or structs. If you are using, for instance, the RethinkDB Elixir library you could just return a list of maps returned by your RethinkDB client. The reason we need to return a list here is that in our query field we stated that we would return a list_of(:user)  or list_of(:post) . If we want to return just one post  or one user  we could have defined a field like so:

In this example we are passing an id  argument to the MyApp.UserResolver.find/2  function in order to return just one user . The resolver function would look something like this:

With that we have all the key pieces in place.

GraphiQL and Routing

In order to play around with our new GraphQL server we are going to add GraphiQL and setup our router to point requests to the new server. Open up web/router.ex  and add the following code:

Now, GraphQL requests can be made to /api  and we can go to http://localhost:4000/graphiql  to play around in the GraphiQL data explorer. Start your server mix phoenix.server  and go to that url in your browser. You should see something like this:

GraphiQL Explorer

Let’s go ahead and add our first query. Type the following query into the query box and hit the play button:

If we want to get the user of each post we could run the following query:

We can also query for users and their posts:

Pretty neat, eh?

One thing to note here is that our server will return data in the exact shape of our query. This is one of the main benefits of GraphQL and allows you to get exactly the data that you need for any particular scenario.

Conclusion

I hope this tutorial was helpful. If you have any issues or just want to say hey feel free to leave a comment below. Also, I’ve created a repo for this tutorial on my github that you can find here. This post ends at the branch checkpoint-1.

You can find the next post in the series here.