AshJido

Hex.pm Hex Docs CI License

AshJido compiles selected Ash APIs into native Jido v3 Actions. It keeps Ash as the source of business rules, authorization, and data access. It uses Jido for execution, Flow orchestration, Agents, and persistence lifecycle.

Main features

AshJido does not add a second Action, Flow, Agent, or Signal runtime.

Installation

Add AshJido to mix.exs:

def deps do
  [
    {:ash_jido, "~> 3.0.0-beta.1"}
  ]
end

Or use Igniter:

mix igniter.install ash_jido

Version 3 uses the Hex releases of jido, jido_action, and jido_signal version 3.

Define the Ash API

Define normal Ash actions and public domain code interfaces:

defmodule MyApp.Accounts.User do
  use Ash.Resource,
    domain: MyApp.Accounts,
    data_layer: AshPostgres.DataLayer

  attributes do
    uuid_primary_key :id
    attribute :name, :string, allow_nil?: false, public?: true
    attribute :email, :string, allow_nil?: false, public?: true
    attribute :secret, :string, sensitive?: true
  end

  actions do
    defaults [:read]

    create :register do
      accept [:name, :email]
    end
  end
end

defmodule MyApp.Accounts do
  use Ash.Domain, extensions: [AshJido]

  resources do
    resource MyApp.Accounts.User do
      define :register_user, action: :register
      define :get_user, action: :read, get_by: [:id]
      define :list_users, action: :read
    end
  end

  jido do
    expose :register_user
    expose :get_user

    expose :list_users,
      filters: [:email],
      sorts: [:name],
      pagination: [type: :offset, max_page_size: 100]
  end
end

This creates these normal Jido Actions:

Run a generated Action

{:ok, output} =
  Jido.Exec.run(
    MyApp.Accounts.Jido.RegisterUser,
    %{name: "Ada", email: "ada@example.com"},
    %{ash: %{actor: current_user, tenant: tenant}}
  )

%{
  result: %{id: id, name: "Ada", email: "ada@example.com"},
  page: nil,
  metadata: nil
} = output

The domain is part of the generated descriptor. Run-time context can contain these context.ash values:

A caller cannot supply a different domain or set authorize?: false.

Stable contracts

Generated Actions use static Zoi schemas. Unsupported custom Ash types fail at compile time unless the exposure supplies a schema_overrides entry.

All successful calls return this shape:

%{
  result: result,
  page: page_or_nil,
  metadata: metadata_or_nil
}

Ash resource structs become maps. The serializer includes public, non-sensitive fields only. It omits unloaded and forbidden fields.

Resource fallback

Use the extension on a resource when a code interface is not available:

defmodule MyApp.Accounts.User do
  use Ash.Resource,
    domain: MyApp.Accounts,
    extensions: [AshJido]

  jido do
    action :register
    action :update_profile, identity: :unique_email
  end
end

This form generates modules under MyApp.Accounts.User.Jido.

Jido Flow

Generated modules are normal Jido Actions. Use them directly in Jido.Flow:

defmodule MyApp.Flows.RegisterAndFetch do
  use Jido.Flow, name: "register_and_fetch"

  flow do
    step "register",
      action: MyApp.Accounts.Jido.RegisterUser,
      params: %{name: input(:name), email: input(:email)}

    step "fetch",
      action: MyApp.Accounts.Jido.GetUser,
      params: %{id: result("register", [:result, :id])}

    output result("fetch")
  end
end

Jido.Exec.run(
  MyApp.Flows.RegisterAndFetch,
  %{name: "Ada", email: "ada@example.com"},
  context
)

AshJido does not provide a Flow wrapper. Flow steps keep separate Ash transaction boundaries. Design compensation in the Flow when a later step can fail.