cclark

cclark

I’m new to Elixir, Phoenix and Absinthe but trying to write an API onto an existing dataset. The dataset includes information about trails and they have a status attribute with values of 1, 2, 3 or 4. In the domain these map to Green (1), Yellow(2), Amber(3) and Red(4) so I’d like to use the actual enum values as it would be much more expressive.

For the GraphQL API onto this it would be much nicer if I could write a query like:


query {

  trails(havingStatus: RED) {

    id

    name

    status

  }

}

In my schema I’ve defined an enum as


 @desc "Current status of the trail"

  enum :trail_status do

    value :green, as: 1, description: "Green: Clear"

    value :yellow, as: 2, description: "Yellow: Minor Issue"

    value :amber, as: 3, description: "Amber: Significant Issue"

    value :red, as: 4, description: "Red: Major Issue"

  end

This allows me to query and enforce that the user chooses valid values for havingStatus. However, in my results I see the raw value of


      {

        "status": 4,

        "name": "Lost Loop",

        "id": "16601"

      }

where I’d really like to have the 3 be returned as RED.

First does this make sense to do? If the client already knows about GREEN, YELLOW, AMBER and RED when making the query then it seems like a failure to make the client know how to receive 1-4 and map it to those values.

It seems like a custom scalar might be one way to get at this. All of the examples I can find are for dates and make sense. But when I have a very specific enumeration of four values I wonder if that is overkill and it seems like I’d lose the nice ability to introspect and see the only valid values. So I think I want a combination of an enum and a custom scalar type?

Any thoughts or pointers to Absinthe/GraphQL articles on the subject would be greatly appreciated.

Showing Posts 1 to 4

kokolegorille

kokolegorille

Hello, welcome to the forum.

I use simple atoms, without as when I don’t need to use db, like so

  enum :sort_order do
    value :asc
    value :asc_nulls_last
    value :asc_nulls_first
    value :desc
    value :desc_nulls_last
    value :desc_nulls_first
  end

But if You want to persists Int in the DB and keep nice looking status, I would use a scalar. Nothing really complicate, You just need a parse and a serialize functions.

I use this for json.

  scalar :json do
    parse fn input ->
      case Jason.decode(input.value) do
        {:ok, result} -> result
        _ -> :error
      end
    end
    serialize &Jason.encode!/1
  end

And with your Enum requirement, it would be simple to write those 2 functions. For example…

scalar :status do
  parse fn 1 -> :green; 2 -> :yellow; 3 -> :amber; 4 -> :red; _ -> :error end
  serialize fn :green -> 1; :yellow -> 2; :amber -> 3; :red -> 4; _ -> :error end
end
cclark

cclark OP

Thanks @kokolegorille! I’m really enjoying Elixir and the forum so far.

Would a scalar be a straight swap out for an enum? From my understanding of your reply it is one or the other and not a way to combine the two.

I tried as per your guidance and am stuck. In my top level schema.ex I took my enum definition of:

  @desc "Current riding status of the trail (defined as an enum)"
  enum :trail_status do
    value :green, as: 1, description: "Green: Clear"
    value :yellow, as: 2, description: "Yellow: Minor Issue"
    value :amber, as: 3, description: "Amber: Significant Issue"
    value :red, as: 4, description: "Red: Major Issue"
  end

and replaced it with your scalar definition (altering it slightly to be trail_status to fit my actual code)

  @desc "Current riding status of the trail (defined as a type)"
  scalar :trail_status do
    parse fn 1 -> :green; 2 -> :yellow; 3 -> :amber; 4 -> :red; _ -> :error end
    serialize fn :green -> 1; :yellow -> 2; :amber -> 3; :red -> 4; _ -> :error end
  end

It probably helps to see my resolver as well…

  def list_trails(%{having_status: status_id}) do
    Trail
    |> where([t], t.status == ^status_id)
    |> Repo.all
  end

Before the switch I was able to run the following query

query {
  trails(havingStatus: GREEN) {
    id
    name
    status
    description
  }
}

It ran without issue and I had the nice constrained/type-ahead completion in GraphiQL that only GREEN, YELLOW, AMBER and RED were allowed.

Now I no longer have the nice type-ahead completion constrained to the valid enum values in GraphiQL but also I’m not able to pass a value to havingStatus: in my query query which results in anything other than an error message saying "Argument \"havingStatus\" has invalid value <VALUE>." where I’ve tried all the following for <VALUE>:

  • raw int: 1
  • int as string: “1”
  • raw values: green, Green, GREEN
  • raw values as strings: “green”, “Green”, “GREEN”
cclark

cclark OP

With some more experimentation I found out where my issue was with getting an enum to work the way I expected, in my object data type definitions I had missed changing a field from :integer to :trail_status. Once I did that I see values like GREEN in my responses instead of 1 and I was able to even use them in a mutation call.

Something still feels a little off because the enum is defined in the GraphQL schema and it does a nice mapping to integer values in the database schema I was given but if I were to do more complex business logic with conditions on the status of a trail then it seems like I’d want some sort of entity to reflect the trail status – but that could be my OO brain still trying to wrap my head around how to do things in Elixir.

StefanL

StefanL

You might want to have a look at EctoEnum, it will probably provide the casting from/to integers in ecto that you are searching for.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews