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.

  • Kaiyes Ansary

    Nice. Thanks for writing this.

  • Very clear example. Thanks for sharing it!

  • Everytime I visit /graphiql I get the error “No query document supplied”, however I did supply an Schema:

    forward “/”, Absinthe.Plug,
    schema: Graphnix.Schema

    forward “/graphiql”, Absinthe.Plug.GraphiQL,
    schema: Graphnix.Schema

    I also get this warning in console:

    warning: this clause cannot match because a previous clause at line 1 always matches
    web/router.ex:1

    What’s going on?

  • Great tutorial! Thank you! 👍

  • collegeimprovements me

    It’s awesome. Thanks for writing this.
    Can you write one tutorial on creating mutation and using query variables also ?

    • Ya that’s in the works as we speak!

  • Pingback: Phoenix GraphQL Tutorial with Phoenix: Add CRUD Using Mutations - Ryan Swapp()

  • Pingback: Phoenix GraphQL Tutorial with Absinthe: Authentication with Guardian - Ryan Swapp()

  • Paul Hovley

    So Ryan, this seems like a really cool solution for managing the data on the client side. Forgive my ignorance since I’m not familiar with Phoenix/Elixir, but how is GraphQL actually gaining a secure connection to the DB to pull the data? I’m not following where that data connection to the database is happening

    • Hey Paul! Glad to hear from you. I hope things are going well.

      Phoenix is a web framework for Elixir (which runs on the Erlang VM). The way that Phoenix establishes a connection to the database is through the Postgrex database driver via a database management library called Ecto. This is all happening on the server so there isn’t a connection to the database from the client. A GraphQL client would make HTTP requests to the Phoenix web server and our GraphQL API would return data to it. If you’re interested in learning more about Elixir, Phoenix, GraphQL, etc… I’m happy to point you in the direction of some good resources! You might want to check out the Elixir lang site http://elixir-lang.org/ or the Phoenix site http://www.phoenixframework.org/docs/overview for more info on either.

      • Paul Hovley

        Thanks Ryan, wanted to pop in and follow what you’ve been working on. Cool stuff! I’ll need to dig around those sites and the documentation to get my bearings a bit more. Keep it up man. Awesome to see you going out of your way to share what you’re learning.

      • Roberto Diaz

        Hi Ryan! It is a great post, it really helped me. However, correct me if I am wrong, Ecto is kind of an ORM, so is there a way of not using Ecto (mix phoenix.new –no-ecto) and use Absinthe? Or I definitely have to use Ecto?. The issue is that I prefer to work on Plain SQL over ORM for perfomance.

        • Thanks! I’m glad it was helpful. You don’t have to use Ecto at all if you don’t want to. In one of my apps I have to get data out of a legacy sybase database and I just query the database directly from my resolver with custom sql. The resolver is where you tell Absinthe how to get the data it should return. You can get that data however you’d like.

          • Brandon Burke

            Hi Ryan, thanks for sharing. Too small of a world not to realize you’re involved with IntakeZen at CSA. I work with the same case management system, and have been developing integrations for several years. Would love to trade experiences, and learn more about how you are using GraphQL at the firm some time.

          • Hi Brandon! We use GraphQL for all the apps we’ve built. It takes a little more work to set up initially but then it requires almost no additional work to add features moving forward 👍 I’m surprised another dev that’s worked with Needles found my blog! There’s not too many of us out there! Thanks for reaching out. Happy to chat about GraphQL any time.

          • Brandon Burke

            Not too many out there at all. I’ve created three more of them and brought them in on my team though, so the number has nearly doubled this year 🙂

            To be honest, I searched GraphQL and Sybase out of curiosity and came across your comment. I figured the chances were probably decent, but then recognized your name from conversations with Oak a couple years ago.

            Would love to chat more about it some time.

  • Pingback: El blog API de El Café de Félix – Primera Parte – hatarakimaseando()

  • Emanuel Quimper

    Awesome post 🙂 Make my day

  • nano

    Wow this is awesome. Thank you for sharing! Checking out the CRUD and Guardian ones next, they look great.

  • Tomo Hung

    Love this post!!
    Just want to add one thing, Poison is Phoenix default JSON parser, so it should be fine not added in mix.exs

  • Dmitriy Nasyrov

    Hey, nice tut! Thanks. Keep posting!

  • Dustin

    Thank you so much! This was really great to see how easy it is to get graphql working in elixir!

    • Thanks! I’m glad you liked it. 🙂

  • Christofer Ärleryd

    Fantastic tutorial!

  • Excellent tutorial! Thanks for posting!

  • Vince Urag

    Thanks Ryan!

  • micosa97

    great!
    You should consider updating tutorial to phoenix 1.3

  • Adam Randall

    Awesome mate, great tutorial.