ash_graphql/documentation/topics/handle-errors.md

115 lines
3.1 KiB
Markdown
Raw Permalink Normal View History

# Handling Errors
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-02 07:03:06 +13:00
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.
```elixir
graphql do
show_raised_errors? true
end
# or it can be done in config
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-02 07:03:06 +13:00
# make sure you've set `otp_app` in your domain, i.e use Ash.Domain, otp_app: :my_app
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-02 07:03:06 +13:00
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.
```elixir
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](https://hexdocs.pm/absinthe/context-and-authentication.html#context-and-plugs) for adding to the absinthe context (i.e for setting a locale).
```elixir
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:
```elixir
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
```
## Custom Errors
If you created your own Errors as described in the [Ash Docs](https://hexdocs.pm/ash/error-handling.html#using-a-custom-exception) you also need to implement
the protocol for it to be displayed in the Api.
```elixir
defmodule Ash.Error.Action.InvalidArgument do
@moduledoc "Used when an invalid value is provided for an action argument"
use Splode.Error, fields: [:field, :message, :value], class: :invalid
def message(error) do
"""
Invalid value provided#{for_field(error)}#{do_message(error)}
#{inspect(error.value)}
"""
end
defimpl AshGraphql.Error, for: Ash.Error.Changes.InvalidArgument do
def to_error(error) do
%{
message: error.message,
short_message: error.message,
code: "invalid_argument",
vars: Map.new(error.vars),
fields: [error.field]
}
end
end
end
```