ash_graphql/documentation/how_to/handle-errors.md
Riccardo Binetti 513c1ac68f
improvement!: port AshGraphql to Ash 3.0 (#123)
Step 1: update Ash

Step 2: mass rename Api to Domain

Step 3: Ash.Query.expr -> Ash.Expr.expr

Also change ref interpolation

Step 4: remove all warnings

Step 5: remove registries from tests

Step 6: fix filter

Step 7: private? -> !public?

Step 8: Ash.Calculation -> Ash.Resource.Calculation

Step 9: use depend_on_resources/1 -> resources/1

Step 10: add Domain to all resources

Step 11: use Ash module for all actions

Step 12: add public? true all around

Step 13: remove verbose? from options passed during Domain calls

Step 14: add simple_sat

Step 15: Ash.ErrorKind is no more, so remove code from errors

Step 16: sprinkle default_accept :* around tests

Step 17: replace Ash.Changeset.new/2 with Ash.Changeset.for_*

Step 18: calculation fixups

- Context is now a struct and arguments go under the arguments key
- Function based calculations receive a list of records
- Add a select to query-based loads
- select -> load

Step 19: pass the correct name to pass the policy in tests

Step 20: Ash.Query.new/2 is no more

Step 21: add AshGraphql.Resource.embedded? utility function

Use that instead of Ash.Type.embedded_type?(resource_or_type) since resources
are not types anymore

Step 22: handle struct + instance_of: Resource in unions

Resources are not type anymore so they need to be passed this way in unions

Step 23: ensure we only check GraphQL actions for pagination

All reads are now paginated by default, so this triggered a compilation error

Step 24: swap arguments for sort on calculations

Step 25: remove unused debug? option
2024-04-01 14:03:06 -04:00

2.2 KiB

Handling Errors

There are various options that can be set on the Domain module to determine how errors behave and/or are shown in the GraphQL.

Showing raised errors

For security purposes, if an error is raised as opposed to returned somewhere, the error is hidden. Set this to true in dev/test environments for an easier time debugging.

graphql do
  show_raised_errors? true
end

# or it can be done in config
# make sure you've set `otp_app` in your domain, i.e use Ash.Domain, otp_app: :my_app

config :my_app, YourDomain, [
  graphql: [
    show_raised_errors?: true
  ]
]

Root level errors

By default, action errors are simply shown in the errors field for mutations. Set this to true to return them as root level errors instead.

graphql do
  root_level_errors? true
end

Error Handler

Setting an error handler allows you to use things like gettext to translate errors and/or modify errors in some way. This error handler will take the error object to be returned, and the context. See the absinthe docs for adding to the absinthe context (i.e for setting a locale).

graphql do
  error_handler {MyApp.GraphqlErrorHandler, :handle_error, []}
end

Keep in mind, that you will want to ensure that any custom error handler you add performs the logic to replace variables in error messages.

This is what the default error handler looks like, for example:

defmodule AshGraphql.DefaultErrorHandler do
  @moduledoc "Replaces any text in message or short_message with variables"

  def handle_error(
        %{message: message, short_message: short_message, vars: vars} = error,
        _context
      ) do
    %{
      error
      | message: replace_vars(message, vars),
        short_message: replace_vars(short_message, vars)
    }
  end

  def handle_error(other, _), do: other

  defp replace_vars(string, vars) do
    vars =
      if is_map(vars) do
        vars
      else
        List.wrap(vars)
      end

    Enum.reduce(vars, string, fn {key, value}, acc ->
      if String.contains?(acc, "%{#{key}}") do
        String.replace(acc, "%{#{key}}", to_string(value))
      else
        acc
      end
    end)
  end
end