Elixir Recipes Absinthe and GraphQL

Absinthe is an Elixir implementation of a GraphQL server. See:

Setup

Add absinthe to your mix.exs, then run mix deps.get.

def apps(_default) do
  [
    :absinthe,
    :absinthe_plug
  ]
end

defp deps do
  [
    {:absinthe, "~> 1.3.2"},
    {:absinthe_plug, "~> 1.3.0"},
    {:absinthe_ecto, "~> 0.1.2"}
  ]
end

Suggested folder structure

graphql
|  mutation
|    user_mutations.ex
|  query
|    user_queries.ex
|  resolver
|    user_resolver.ex
|  type
|    user_types.ex
|  schema.ex
|  graphql.ex

Sample Routes

scope "/" do
  # Your plugs and other stuff here

  if Mix.env == :dev do
    forward "/graphiql", Absinthe.Plug.GraphiQL, schema: MyApp.GraphQL.Schema
  else
    forward "/graphql", Absinthe.Plug, schema: MyApp.GraphQL.Schema
  end
end

Sample GraphQL Module

defmodule MyApp.GraphQL do
  @moduledoc """
  A module that keeps using definitions for graphql components.
  """

  def type do
    quote do
      use Absinthe.Schema.Notation
      use Absinthe.Ecto, repo: MyApp.Repo
    end
  end

  def query do
    quote do
      use Absinthe.Schema.Notation
    end
  end

  def schema do
    quote do
      use Absinthe.Schema
    end
  end

  def resolver do
    quote do
      alias MyApp.Repo
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end

Sample Schema

defmodule MyApp.GraphQL.Schema do
  use MyApp.GraphQL, :schema
  alias MyApp.GraphQL.{
    Query.UserQueries,
    Mutation.UserMutations.
    Type.UserType
  }

  # Import everything on the schema since Absinthe will throw an error on compile if you have multiple imports of the same module spread across multiple files
  import_types UserQueries
  import_types UserMutations
  import_types UserType

  query do
    import_fields :user_queries
  end

  mutation do
    import_fields :user_mutations
  end
end

Sample Query

defmodule MyApp.GraphQL.Query.UserQueries do
  use MyApp.GraphQL, :query
  alias MyApp.GraphQL.Resolver.UserResolver

  object :user_queries do
    @desc "Get all users"
    field :users, list_of(:user) do
      resolve &UserResolver.all/2
    end

    @desc "Get user with id"
    field :user, type: :user do
      arg :id, non_null(:id)
      resolve &UserResolver.find/2
    end
  end
end

Sample Type

defmodule MyApp.GraphQL.Type.UserType do
  use MyApp.GraphQL, :type

  object :user do
    field :id, :id
    field :first_name, :string
    field :last_name, :string
    field :email, :string

    field :projects, list_of(:project), resolve: assoc(:projects) # Defined under project_type.ex
  end
end

Sample Resolver

defmodule MyApp.GraphQL.Resolver.UserResolver do
  use MyApp.GraphQL, :resolver
  alias MyApp.User

  def all(_args, _info) do
    {:ok, Repo.all(User)}
  end

  def find(%{id: id}, _info) do
    case Repo.get(User, id) do
      nil -> {:error, "User not found"}
      user -> {:ok, user}
    end
  end
end