Absinthe: change resolution of parent field based on child field possible?

Hey good folks, i’m stuck on an Absinthe issue and could use some perspective.

I have a graphql query similar to this (simplified):

query($id: ID!) {
  org(id: $id) {
    successful
    result {
      name
      accounts {
        email
      }
    }
    messages {
      field
      code
    }
  }
}

The successful, result and messages fields are used with absinthe_error_payload, which is useful to map changeset errors.

In case the user does not have permission to query the org, they will get a response such as:

successful: false,
result: nil,
messages: [{
  field: "org"
  code: "permission_denied"
}]

This is working successfully. So far, so good.

Now we have the scenario where a user has permission to query the org, but does not have permission to view the nested accounts. We don’t know ahead of time whether the query will include the accounts field, and so it has its own resolver which checks permissions. In case of permission denied, I would like to bubble up the error to be collected on the parent org field, so we will end up with this:

successful: false,
result: nil,
messages: [{
  field: "org.accounts"
  code: "permission_denied"
}]

I want it so that if any nested field has an error, the entire query has an error and no results are returned.

I have found that the top level org field must first be resolved before its nested account field can be resolved. This makes sense, since the parent field is passed to the nested ones in the resolver. There doesn’t seem to be any way to bubble up the error to the top.

I tried creating a middleware similar to Absinthe.Middleware.Async to redo the resolution phase, however this doesn’t seem to help considering the org field is already in the :resolved state by this time.

Any tips as to how I may achieve this?

This is really the opposite of how Absinthe.Error.Payload works through right? Its whole premise is that it maps the error values of a field to results at the field level. I would consider using regular actual {:error tuples from within resolvers since that will bubble the error up to the top in a way similar to what you asked.

This will sort of happen if you make every single field null: false, but otherwise this would be a spec violation.

1 Like

Hi @benwilson512, you’re right. What I was trying to do goes against the grain.

I am planning to move forward with the following approach:

  • For permission errors on nested fields, use the built in absinthe / gql error handling for that, and they will naturally bubble to the top.

  • In cases where I want to handle specific errors in nested fields, this is usually because the front-end wants to do something with it. The errors can be treated as data and left at the level of the nested field. This can be achieved either using Absinthe.Error.Payload on those nested fields, or I may explore union fields instead.

  • Use Absinthe.Error.Payload only for mutations, and not for queries (in most cases)

Thank you for your help, much appreciated!