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:

mix.exs def application do [mod: {MyApp, []}, applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, :phoenix_ecto, :postgrex, :absinthe, :absinthe_plug, :absinthe_ecto, :poison, :faker]] end # Code omitted defp deps do [{:phoenix, "~> 1.2.1"}, {:phoenix_pubsub, "~> 1.0"}, {:phoenix_ecto, "~> 3.0"}, {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.6"}, {:phoenix_live_reload, "~> 1.0", only: :dev}, {:gettext, "~> 0.11"}, {:cowboy, "~> 1.0"}, {:absinthe, "~> 1.2.0"}, {:absinthe_plug, "~> 1.1"}, {:absinthe_ecto, git: "https://github.com/absinthe-graphql/absinthe_ecto.git"}, {:poison, "~> 2.1.0"}, {:faker, "~> 0.7"}] end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def application do [ mod : { MyApp , [ ] } , applications : [ : phoenix , : phoenix_pubsub , : phoenix_html , : cowboy , : logger , : gettext , : phoenix_ecto , : postgrex , : absinthe , : absinthe_plug , : absinthe_ecto , : poison , : faker ] ] end # Code omitted defp deps do [ { : phoenix , "~> 1.2.1" } , { : phoenix_pubsub , "~> 1.0" } , { : phoenix_ecto , "~> 3.0" } , { : postgrex , ">= 0.0.0" } , { : phoenix_html , "~> 2.6" } , { : phoenix_live_reload , "~> 1.0" , only : : dev } , { : gettext , "~> 0.11" } , { : cowboy , "~> 1.0" } , { : absinthe , "~> 1.2.0" } , { : absinthe_plug , "~> 1.1" } , { : absinthe_ecto , git : "https://github.com/absinthe-graphql/absinthe_ecto.git" } , { : poison , "~> 2.1.0" } , { : faker , "~> 0.7" } ] end

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:

$ mix phoenix.gen.model User users name:string email:string $ mix ecto.migrate 1 2 3 $ mix phoenix . gen . model User users name : string email : string $ mix ecto . migrate

Second, add a post model and run the migration:

$ mix phoenix.gen.model Post posts title:string body:text user_id:references:users $ mix ecto.migrate 1 2 3 $ mix phoenix . gen . model Post posts title : string body : text user_id : references : users $ mix ecto . migrate

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:

web/models/user.ex schema "users" do field :name, :string field :email, :string has_many :posts, MyApp.Post timestamps() end 1 2 3 4 5 6 7 schema "users" do field : name , : string field : email , : string has_many : posts , MyApp . Post timestamps ( ) end

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

priv/repo/seeds.exs alias MyApp.User alias MyApp.Post alias MyApp.Repo Repo.insert!(%User{name: "Ryan Swapp", email: "ryan@ryan.com"}) Repo.insert!(%User{name: "Rosie", email: "rosie@mydog.com"}) for _ <- 1..10 do Repo.insert!(%Post{ title: Faker.Lorem.sentence, body: Faker.Lorem.paragraph, user_id: [1, 2] |> Enum.take_random(1) |> hd }) end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 alias MyApp . User alias MyApp . Post alias MyApp . Repo Repo . insert ! ( % User { name : "Ryan Swapp" , email : "ryan@ryan.com" } ) Repo . insert ! ( % User { name : "Rosie" , email : "rosie@mydog.com" } ) for _ < - 1..10 do Repo . insert ! ( % Post { title : Faker . Lorem . sentence , body : Faker . Lorem . paragraph , user_id : [ 1 , 2 ] | > Enum . take_random ( 1 ) | > hd } ) end

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:

web/schema/types.ex defmodule MyApp.Schema.Types do use Absinthe.Schema.Notation use Absinthe.Ecto, repo: MyApp.Repo object :user do field :id, :id field :name, :string field :email, :string field :posts, list_of(:post), resolve: assoc(:posts) end object :post do field :id, :id field :title, :string field :body, :string field :user, :user, resolve: assoc(:user) end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 defmodule MyApp . Schema . Types do use Absinthe . Schema . Notation use Absinthe . Ecto , repo : MyApp . Repo object : user do field : id , : id field : name , : string field : email , : string field : posts , list_of ( : post ) , resolve : assoc ( : posts ) end object : post do field : id , : id field : title , : string field : body , : string field : user , : user , resolve : assoc ( : user ) end end

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:

web/schema.ex defmodule MyApp.Schema do use Absinthe.Schema import_types MyApp.Schema.Types query do field :posts, list_of(:post) do resolve &MyApp.PostResolver.all/2 end field :users, list_of(:user) do resolve &MyApp.UserResolver.all/2 end end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 defmodule MyApp . Schema do use Absinthe . Schema import_types MyApp . Schema . Types query do field : posts , list_of ( : post ) do resolve & MyApp . PostResolver . all / 2 end field : users , list_of ( : user ) do resolve & MyApp . UserResolver . all / 2 end end end

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:

web/resolvers/post_resolver.ex defmodule MyApp.PostResolver do alias MyApp.Repo alias MyApp.Post def all(_args, _info) do {:ok, Repo.all(Post)} end end 1 2 3 4 5 6 7 8 defmodule MyApp . PostResolver do alias MyApp . Repo alias MyApp . Post def all ( _args , _info ) do { : ok , Repo . all ( Post ) } end end

web/resolvers/user_resolver.ex defmodule MyApp.UserResolver do alias MyApp.Repo alias MyApp.User def all(_args, _info) do {:ok, Repo.all(User)} end end 1 2 3 4 5 6 7 8 defmodule MyApp . UserResolver do alias MyApp . Repo alias MyApp . User def all ( _args , _info ) do { : ok , Repo . all ( User ) } end end

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:

web/schema.ex field :user, type: :user do arg :id, non_null(:id) resolve &MyApp.UserResolver.find/2 end 1 2 3 4 field : user , type : : user do arg : id , non_null ( : id ) resolve & MyApp . UserResolver . find / 2 end

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:

web/resolvers/user_resolver.ex def find(%{id: id}, _info) do case Repo.get(User, id) do nil -> {:error, "User id #{id} not found"} user -> {:ok, user} end end 1 2 3 4 5 6 def find ( % { id : id } , _info ) do case Repo . get ( User , id ) do nil -> { : error , "User id #{id} not found" } user -> { : ok , user } end end

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:

web/router.ex scope "/", MyApp do pipe_through :browser # Use the default browser stack get "/", PageController, :index end forward "/api", Absinthe.Plug, schema: MyApp.Schema forward "/graphiql", Absinthe.Plug.GraphiQL, schema: MyApp.Schema 1 2 3 4 5 6 7 8 9 10 11 scope "/" , MyApp do pipe_through : browser # Use the default browser stack get "/" , PageController , : index end forward "/api" , Absinthe . Plug , schema : MyApp . Schema forward "/graphiql" , Absinthe . Plug . GraphiQL , schema : MyApp . Schema

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:

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

{ posts { title, body } } 1 2 3 4 5 6 { posts { title , body } }

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

{ posts { title, body, user { name } } } 1 2 3 4 5 6 7 8 9 { posts { title , body , user { name } } }

We can also query for users and their posts:

{ users { name, posts { title } } } 1 2 3 4 5 6 7 8 { users { name , posts { title } } }

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.