michaelcaterisano
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.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
benwilson512
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
createEntitiesfield 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:
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:
Then you can return this sort of thing in your mutation resolver:
Then your
entity_resultobject would look like this:This will produce results that look basically like this:
Again happy to be wrong, but the actual resolution part of the spec seems pretty clear to me.
michaelcaterisano
@benwilson512 While your suggestion would work and is a workaround we’re considering, we’d really like to return a single payload type with some top-level fields, like this:
To your point about this sentence:
The interpretation that an error raised while resolving a list of nullable types should cause the entire field to return
nullseems to make it impossible to return a list of nullable types whose values are a mixture of data andnull, adding errors to the error key, as described here:I think the question is whether list items are considered fields. The quote above seems to suggest that they are, no?
benwilson512
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:
Notice how in the mutation resolver result item at index
1is%{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.michaelcaterisano
Yeah sorry about that, I caught that after I wrote it and edited my post above.
Given the interpretation of the spec you’re describing, I’m not clear on how the response described in the List section of the spec would ever be possible. What am I missing? The following suggests that a response like
[item, null, item]and an error on the errors key related to thenullresult should be possible:I tried to find a clear definition of “preparation” in the spec, but I only found “preparation” and “preparing” used in two places, and both are a bit vague. I guess we’re saying that it relates specifically to type coercion? IMO it’s not fully clear what the spec means by “preparation”. It seems to me that explicitly returning
nullfor an element in a list should be possible.And regarding this:
Isn’t it true that for each entity in a list
[Entity], the query resolver for entity will be called?benwilson512
Apologies I’m on my phone now so I can’t construct an example of the coercion scenario I’m talking about. I believe the idea is that if you return like
[%{foo: 1}, 1, %{foo: 2}]the middle integer cannot coerce to an object, so you get an error.Which resolver? Can you construct a minimal example that shows the resolution behavior you expect?
michaelcaterisano
This seems right to me.
A naive implementation of my scenario would look like this:
The mutation operation would look like this:
The response in this case would be:
In other words, the field resolvers for
entityare called for each element in theentitieslist, which in this case uppercases the value oftext. Is this not resolution?UPDATE:
@benwilson512 I think I see the problem I’m having. In order to create the response I want, I’d need to have data at
createEntities.entitiesand also errors whose paths arecreateEntities.entities. In my example above, the error path would becreateEntitites.entities.textif the text resolver returned an error.It does seem unfortunate that the spec has this limitation in the case of a field whose value is a list of nullable types, though. The inability to return a list that describes partial success and also return error describing the failures leads to workarounds like returning errors as data, which IMO violates the spec.
We’ll probably end up doing what you suggested; that is, returning a list of payload types instead of a single payload type whose fields are lists. Thanks for the discussion.
benwilson512
Yes, this is exactly the issue. If you have:
There are only three fields here.
createEntitiestakes the root object, and returns a singlecreate_entities_payloadobjectentitiestakes a singlecreate_entities_payloadand returns a list of entities.texttakes an individual entity, and returns a single string.If you return an error on
(3)it’s really just on the text field, but this is pretty close to what I was having you do with the wrapper object.If you return an error on
(2)you void the whole list, and obviously the same issue applies to(1).I don’t disagree, it’s definitely annoying. I wonder if some of this has to do with the whole mental model around a “graph”. You either traverse the node to the type or you don’t. Not sure! Either way, let me know if you run into any snags in your wrapper effort, happy to help.
Likewise!
Rumata
I just found this topic and compared it to the old spec GraphQL, and it suggests partial responses containing array indexes like this:
and
The latter seems to be exactly the type the topic starter wanted to have.
The latest version of the spec hasn’t change any of these: GraphQL
benwilson512
I am always happy to correct spec non compliance in Absinthe. However so far all that has happened is that people see a specific output and believe that they should be able to produce that output form a specific high level resolver. This is not what the spec states, nor is it even what the output states, as covered by earlier messages in the thread.
I don’t mean to be rude but this sort of looping is not productive. Absinthe has been producing the output you link for many years.
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.