AshJido
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
- Domain-first compilation from Ash code interfaces
- Resource action fallback for APIs that do not have code interfaces
- Static Zoi input and output schemas
- Native execution with
Jido.Exec - Native composition with
Jido.Flow - Fixed compile-time domains and namespaced Ash context
- Public, non-sensitive result serialization
- Explicit filter, sort, load, and pagination allowlists
- Ash-backed Jido persistence with atomic compare-and-swap
- Ash notifier publications to a Jido Signal bus
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:
MyApp.Accounts.Jido.RegisterUserMyApp.Accounts.Jido.GetUserMyApp.Accounts.Jido.ListUsers
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:
actortenantscopetracercontexttimeoutauthorize?: true
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.