Hex version HexDocs

Define your application's error vocabulary with a reusable Elixir error struct. RintoError validates declared kinds, builds transport-independent public payloads, and formats exception and log text. Public names and HTTP statuses are optional; registered custom structs and Ecto changesets can supply their own error details. Both use defimpl RintoError.Source with build/1 and to_payload/2 through protocol dispatch. The supplied Changeset implementation can be excluded at compile time so your application can provide its own (see the Ecto guide).

Why start at 0.2.0?

RintoError's first public release is 0.2.0. This is a deliberate release decision: the core error model, integration paths, tests, and bilingual guides form the baseline we have chosen to publish and maintain. The version marks that starting point; it does not imply a previously published 0.1.0 or a 1.0 API stability guarantee.

Installation

Requires Elixir ~> 1.17. Add RintoError to your dependencies and run mix deps.get:

def deps do
  [
    {:rinto_error, "~> 0.2.0"}
  ]
end

Add :rinto_error to import_deps in .formatter.exs for catalog formatting:

[
  import_deps: [:rinto_error]
]

Minimal usage

Return a business error

Declare only the kinds your application needs. Public names and HTTP statuses are optional:

defmodule MyApp.Error do
  use RintoError

  catalog do
    entry :payment_timeout
  end
end

defmodule MyApp.Payments do
  require MyApp.Error, as: Error

  def interpret_result({:ok, receipt}), do: {:ok, receipt}
  def interpret_result({:error, :timeout}), do: {:error, Error.new(:payment_timeout)}
end

The function returns an ordinary error tuple containing your Error struct, so callers can match on its kind:

{:error, %MyApp.Error{kind: :payment_timeout}} =
  MyApp.Payments.interpret_result({:error, :timeout})

Error.new/1 validates the kind and records the construction location. This example needs no HTTP or payload configuration and does not raise or log.

Respond to an error in Phoenix

For an HTTP application, extend the catalog with an optional public name and status (use this definition in place of the one above):

defmodule MyApp.Error do
  use RintoError

  catalog do
    entry :payment_timeout, as: :payment_unavailable, http_status: 503
  end
end

In an existing Phoenix application, a fallback controller can turn the same {:error, error} result into an HTTP response:

defmodule MyAppWeb.FallbackController do
  use MyAppWeb, :controller
  alias MyApp.Error

  def call(conn, {:error, %Error{} = error}) do
    conn
    |> put_status(Error.http_status(error))
    |> json(Error.to_payload(error))
  end
end

Set action_fallback MyAppWeb.FallbackController in the calling controller and return {:error, error} from its action. For the timeout above, the response has status 503 and this JSON body:

{"kind":"payment_unavailable","data":{},"errors":[]}

Business code still sees :payment_timeout; as: changes only the public name. RintoError supplies the status and payload, while Phoenix sends the response.

Guides / 使用指南

Read these guides in order, or jump to the integration you need. Each guide is available in English and Chinese.

GuideEnglish中文
1. Design and error modelEnglish设计思想与错误模型
2. Application errorsEnglish定义与使用应用错误
3. Ecto validationEnglishEcto 校验错误实践
4. Custom error structsEnglish接入自定义错误 struct
5. Output and loggingEnglish输出、日志与应用边界

For API contracts and options, see RintoError, RintoError.Source, and RintoError.Changeset in the API documentation.

License

MIT.