michaelcaterisano

michaelcaterisano

Unable to return errors when List [T] contains null values

I’m trying to do the same thing described in this issue: Allow returning data and errors from a resolver · Issue #512 · absinthe-graphql/absinthe · GitHub

That is, I want to create a mutation field whose return type looks like this:

type MutationPayload {
  entities: [Entity]
}

And handle partial success with a response like this:

{
  data: {
    createEntities: {
     entities: [ {text: "hi"}, null, {text: "hello"} ]
   }
  }, 
  errors: [{ message: "some error related to entity with id 123" }]
}

In the linked issue above, @benwilson512 says that this response is not allowed by the spec. I’ve been discussing this subject with my team, and there is some doubt about this interpretation. I think it’s informed by the Response section of the spec, specifically this paragraph:If an error was encountered during the execution that prevented a valid response, the data entry in the response should be null.

However, the List section seems to suggest that the response described above is in fact valid: If a list’s item type is nullable, then errors occuring during preparation or coercion of an individual item in the list must result in a the value null at that position in the list along with an error added to the response.

The Absinthe library disallows this response, as described in the github issue. I can confirm that this is the case. Do folks have any thoughts about how to approach this? It seems to me that the library isn’t allowing something that should be allowed by the spec, and I’m hoping to find a workaround.

First Post!

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hi @michaelcaterisano welcome!

I could be mistaken, but I think you’re conflating two distinct things. The section you have linked to is entitled “Result coercion” and it has to do with coercing a result from the internal programming language value into a GraphQL type. It isn’t speaking directly to the question of whether a resolver on the createEntities field can return both data and errors.

This section here describes how resolvers ought to work, and if you scroll down to here you get the bit I rely on for interpretation:

If an error is thrown while resolving a field, it should be treated as though the field returned null, and an error must be added to the “errors” list in the response.

That doesn’t seem to leave a lot of room to do what you want as a return value from a resolver. Now, what you can do is basically:

type MutationPyaload {
  entities: [EntityResult]
}

type EntityResult {
  entity: Entity!
}

Then you can return this sort of thing in your mutation resolver:

result = [%{entity: entity1}, %{error: {:error, "couldn't make entity 123"}}, %{entity: entity3}]

{:ok, result}

Then your entity_result object would look like this:

object :entity_result do
  field :entity, :entity, resolve fn
    %{entity: entity}, _, _ -> {:ok, entity}
    %{error: error}, _, _ -> error
  end
end

This will produce results that look basically like this:

{
  data: {
    createEntities: {
     entities: [ {entity: {text: "hi"}}, null, {entity: {text: "hello"}} ]
   }
  }, 
  errors: [{ message: "some error related to entity with id 123"}]
}

Again happy to be wrong, but the actual resolution part of the spec seems pretty clear to me.

Most Liked

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Just to be clear, my solution returns errors as errors to the client, not as data. It is returning errors as part of the Elixir resolver return value sure, but then it’s converted to a GraphQL error here:

  field :entity, :entity, resolve fn
    %{entity: entity}, _, _ -> {:ok, entity}
    %{error: error}, _, _ -> error
  end

Notice how in the mutation resolver result item at index 1 is %{error: {:error, "reason"}} and so the value is unpacked from the %{error: key it means that the resolver is returning {:error, "reason"} which means you get a proper GraphQL error.

That is the question, and they definitely are not, nor is “coercion” or “preparation” the same as resolution or execution. If you read through the “Executing fields” section GraphQL I think it’s pretty clear that a given item in a list cannot also be a field. It doesn’t take arguments, it doesn’t have a distinct selection set, etc.

The other thing to emphasize is that we’re talking about the resolution of a specific mutation field. That field has a single return value. That value can be a list type, but that list type is not then N resolutions, where there is a distinct resolution for each item in the list. Rather there is one resolution (the resolution of the mutation field) that returns one value (a list), there is a “result coercion” of that value, which involves a result coercion of each item in the list. Then fields in the selection set on the result are executed on each result. There is no field that represents individual item from parent list -> individual item | null | errors, nor is there a resolver for such.

Last Post!

Rumata

Rumata

I’m sorry if my comment looked rude.

I was looking for a way to return partial data along with some additional error signalling that the user was forbidden from accessing it, which I remembered was a “blessed” way of doing that somewhere in the official docs. And after looking through the Absinthe docs and finding no way to do that I went on to search for a solution over the web and found the issue on GitHub and this topic.

Please don’t get me wrong. Absinthe is great, it helps a lot of people and doesn’t cost anything to use it. We take it for granted but if think about the effort it took to build it to the current state, we’ll realize it’s huge.

Of course libraries don’t have to be fully compliant to the standards. It often costs 80% of effort (5 times more) to reach the last 20% of compliance.

I just wanted to support others who try to do the same thing and find this topic, prove that what they want is a sensible thing and is not against the standard. And maybe some of those who find this topic later will be courageous and will have enough time to implement it as a feature for the benefit of us all.

Where Next?

Popular in Questions Top

minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement