Define a Graphql Enum Type in Ash


Here's how to define an enum type in Ash that's exposed in Graphql.

Adhoc enum

Adhoc enums are great, because they're easy and quick to define, and the constraints are enforced. Here's what that definition looks like:

defmodule MyLibrary.Shelves.BookReview do
  use Ash.Resource,
    domain: MyLibrary.Shelves,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshGraphql.Resource]

  attributes do
    # ...
    attribute :rating, :atom do
      constraints one_of: [:one, :two, :three, :four, :five]
      public? true
    end
  end
end

When you create such a book review in graphql, for instance (ash_graphql integration not shown above), the mutation would look something like this:

mutation rateBook($rating: String!) {
  rateBook(input: {
    rating: $rating
  }) {
    result {
      rating
    }
  }
}

Note the type here. It's a string. This means that it's not necessarily clear from graphql introspection what the possible values for rating are, even though we've constrained them on the back end.

One way to make this more explicit and clear is to create an enum.

Expose Ash Enum Type in Graphql

This enum, when used instead of a plain-jane atom, will also be visible via graphql introspection. Let's adjust our rating attribute from above, in part:

attributes do
  attribute :rating, MyLibrary.Shelves.Stars do
    public? true
  end
end

Note that the type is not :atom now. It's a new custom module. Importantly, this module must exist either before this module definition or in a separate file so that it's available at compile time. Otherwise, you'll get a compiler error.

Here's an enum:

defmodule MyLibrary.Shelves.Stars do
  use Ash.Type.Enum, values: [:one, :two, :three, :four, :five]

  def graphql_type(_), do: :star
end

Pretty straightforward. Now when you mutate, you'll use a new Star! type, like this:

mutation rateBook($rating: Star!) {
  rateBook(input: {
    rating: $rating
  }) {
    result {
      rating
    }
  }
}

And now Star can be introspected to see that only the values of 'one', 'two', 'three', 'four' or 'five' are allowed. It's helpful.

There are other optional functions that can be implemented from Ash.Type.Enum for translating valid enum values or providing documentation. Those are not pictured.

But picture your next API with some beautiful enums. Oooooo...