Packages
ash_json_api
1.6.4
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0-rc.2
1.6.0-rc.1
1.6.0-rc.0
1.5.1
1.5.0
1.4.45
1.4.44
1.4.43
1.4.42
1.4.41
1.4.40
1.4.39
1.4.38
1.4.37
1.4.36
1.4.35
1.4.34
1.4.33
1.4.32
1.4.31
1.4.30
1.4.29
1.4.28
1.4.27
1.4.26
1.4.25
1.4.24
1.4.23
1.4.22
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
1.4.15
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.8
1.3.7
1.3.6
1.3.5
1.3.4
retired
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.34.2
0.34.1
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.3
0.31.2
retired
0.31.1
0.31.0
0.30.1
0.30.0-rc.4
0.30.0-rc.3
0.30.0-rc.2
0.30.0-rc.1
0.30.0-rc.0
0.29.1
0.29.0
0.28.6
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.6
0.27.5
0.27.1
0.27.0
0.25.0
0.24.4
0.24.2
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.1
0.10.0
0.9.0
0.8.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.4
0.2.3
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
The JSON:API extension for the Ash Framework.
Current section
Files
Jump to
Current section
Files
documentation/topics/errors.md
<!--
SPDX-FileCopyrightText: 2020 Zach Daniel
SPDX-License-Identifier: MIT
-->
# Errors
AshJsonApi converts Ash errors into [JSON:API error objects](https://jsonapi.org/format/#errors). This topic covers how that conversion works, how to customize it, and the available configuration options.
## Error Format
Every error response follows the JSON:API error object format:
```json
{
"errors": [
{
"id": "a1b2c3d4-...",
"status": "422",
"code": "invalid_attribute",
"title": "InvalidAttribute",
"detail": "must be present",
"source": {
"pointer": "/data/attributes/name"
}
}
]
}
```
## The `AshJsonApi.ToJsonApiError` Protocol
AshJsonApi uses the `AshJsonApi.ToJsonApiError` protocol to convert Ash exceptions into `AshJsonApi.Error` structs. Built-in implementations are provided for common Ash errors such as `Ash.Error.Changes.InvalidChanges`, `Ash.Error.Query.NotFound`, `Ash.Error.Forbidden.Policy`, and others.
If your application raises a custom Ash exception and you want it to produce a specific JSON:API error, implement the protocol:
```elixir
defimpl AshJsonApi.ToJsonApiError, for: MyApp.Errors.PaymentRequired do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 402,
code: "payment_required",
title: "PaymentRequired",
detail: error.message,
meta: %{}
}
end
end
```
The `AshJsonApi.Error` struct has the following fields:
| Field | Description |
|---|---|
| `id` | Unique identifier for this error occurrence |
| `status_code` | HTTP status code (integer) |
| `code` | Machine-readable error code string |
| `title` | Human-readable error title |
| `detail` | Human-readable explanation specific to this occurrence |
| `source_pointer` | JSON Pointer to the source of the error (e.g. `/data/attributes/name`) |
| `source_parameter` | Query parameter that caused the error |
| `meta` | Arbitrary metadata map |
| `about` | Link to further information about this error |
| `log_level` | Log level for this error (default: `:debug`) |
| `internal_description` | Internal description used for logging, not sent to clients |
## Transforming Errors with `error_handler`
The `error_handler` domain option lets you intercept and transform any `AshJsonApi.Error` struct before it is sent to the client. This is useful for sanitizing error messages, adding metadata, translating error text, or applying any other cross-cutting transformation.
Configure it in your domain as an MFA:
```elixir
defmodule MyApp.Domain do
use Ash.Domain, extensions: [AshJsonApi.Domain]
json_api do
error_handler {MyApp.JsonApiErrorHandler, :handle_error, []}
end
end
```
The handler receives the `AshJsonApi.Error` struct and a context map, and must return a modified `AshJsonApi.Error` struct:
```elixir
defmodule MyApp.JsonApiErrorHandler do
def handle_error(error, _context) do
# Sanitize internal details from 500 errors
if error.status_code >= 500 do
%{error | detail: "An internal error occurred. Please try again later."}
else
error
end
end
end
```
The context map contains:
| Key | Description |
|---|---|
| `:domain` | The domain module handling the request |
| `:resource` | The resource module associated with the request (may be `nil`) |
### Example: Translating Error Messages
```elixir
defmodule MyApp.JsonApiErrorHandler do
def handle_error(error, _context) do
%{error | detail: MyApp.Gettext.translate_error(error.code, error.detail)}
end
end
```
### Example: Adding Custom Metadata
```elixir
defmodule MyApp.JsonApiErrorHandler do
def handle_error(error, %{domain: domain}) do
%{error | meta: Map.put(error.meta || %{}, :api_version, "v2")}
end
end
```
### Example: Context-Specific Handling
```elixir
defmodule MyApp.JsonApiErrorHandler do
def handle_error(error, %{resource: resource}) do
case resource do
MyApp.PaymentResource ->
%{error | detail: MyApp.Payments.format_error(error)}
_ ->
error
end
end
end
```
## Configuration Options
### `show_raised_errors?`
By default, if an error is *raised* (i.e. an unexpected exception, not a structured Ash error), AshJsonApi returns a generic error message with only a UUID for reference. This prevents leaking internal implementation details.
Set `show_raised_errors? true` to include the full exception in the response — useful during development:
```elixir
json_api do
show_raised_errors? true
end
```
### `log_errors?`
Controls whether errors are logged. Defaults to `true`.
```elixir
json_api do
log_errors? false
end
```
### Policy Breakdown Details
By default, authorization failures return a generic "forbidden" message. To include a breakdown of which policies failed (useful for debugging), set this in your application config:
```elixir
# config/dev.exs
config :ash_json_api, :policies, show_policy_breakdowns?: true
```
> **Warning:** Do not enable this in production, as it may expose details about your authorization logic.