Skip to content
Back to skills

Ash Framework

ASecurity

Comprehensive Ash framework guidelines for Elixir applications. Use when working with Ash resources, domains, actions, queries, changesets, policies, calculations, or aggregates. Covers code interfaces, error handling, validations, changes, relationships, and authorization. Read documentation before using Ash features - do not assume prior knowledge.

  • 57 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 31, 2026
documentationtestingdocumentation

Security analysis

A100/100

Scanned May 31, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill ash-framework --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ash Framework?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ash Framework
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-ash-framework/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-ash-framework)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ash-framework
description: Comprehensive Ash framework guidelines for Elixir applications. Use when working with Ash resources, domains, actions, queries, changesets, policies, calculations, or aggregates. Covers code interfaces, error handling, validations, changes, relationships, and authorization. Read documentation before using Ash features - do not assume prior knowledge.
---

# Ash Framework Guidelines

Ash is a declarative framework for modeling domains with resources. **Read documentation before using features.**

## Code Interfaces

Define code interfaces on domains - avoid direct `Ash.get!/2` calls in LiveViews/Controllers:

```elixir
# In domain
resource Post do
  define :get_post, action: :read, get_by: [:id]
  define :list_posts, action: :read
  define :create_post, action: :create, args: [:title]
end

# Usage - prefer query option over manual Ash.Query building
posts = MyApp.Blog.list_posts!(
  query: [filter: [status: :published], sort: [published_at: :desc], limit: 10],
  load: [author: :profile, comments: [:author]]
)

post = MyApp.Blog.get_post!(id, load: [comments: [:author]])
```

**Authorization functions** are auto-generated: `can_create_post?(actor)`, `can_update_post?(actor, post)`.

**Using scopes**: Pass `scope: socket.assigns.scope` in LiveViews; use `context` parameter in hooks.

## Actions

- Create specific, well-named actions (not generic CRUD)
- Put business logic inside action definitions
- Use `before_action/after_action` for same-transaction logic
- Use `before_transaction/after_transaction` for external calls

```elixir
actions do
  create :sign_up do
    argument :invite_code, :string, allow_nil?: false
    change set_attribute(:joined_at, &DateTime.utc_now/0)
    change relate_actor(:creator)
  end
end
```

## Querying

**Important**: `Ash.Query.filter/2` is a macro - you must `require Ash.Query`:

```elixir
require Ash.Query

Post
|> Ash.Query.filter(status == :published)
|> Ash.Query.sort(published_at: :desc)
|> Ash.Query.load([:author, :comments])
|> Ash.Query.limit(10)
|> Ash.read!()
```

## Error Handling

- Use `!` variations (`Ash.create!`, `Domain.action!`) when expecting success
- Prefer `!` over pattern matching `{:ok, result} = ...`
- Error classes: `Forbidden` > `Invalid` > `Framework` > `Unknown`

## Validations

```elixir
# Built-in validations
validate compare(:age, greater_than_or_equal_to: 18)
validate match(:email, ~r/@/)
validate one_of(:status, [:active, :pending])

# Conditional validation
validate present(:phone) do
  where eq(:contact_method, "phone")
end

# Skip if prior validations failed
validate expensive_check() do
  only_when_valid? true
end
```

**Avoid redundant validations** - don't duplicate attribute constraints (`allow_nil? false` already validates presence).

## Changes

```elixir
# Built-in changes
change set_attribute(:status, "pending")
change relate_actor(:creator)
change atomic_update(:counter, expr(counter + 1))

# Custom change module
defmodule MyApp.Changes.Slugify do
  use Ash.Resource.Change

  def change(changeset, _opts, _context) do
    title = Ash.Changeset.get_attribute(changeset, :title)
    slug = title |> String.downcase() |> String.replace(~r/[^a-z0-9]+/, "-")
    Ash.Changeset.change_attribute(changeset, :slug, slug)
  end
end
```

**Prefer custom modules** over anonymous functions for changes, validations, preparations.

## Preparations

Modify queries before execution:

```elixir
prepare build(sort: [created_at: :desc])
prepare build(filter: [deleted: false])

# Conditional preparation
prepare build(filter: [visible: true]) do
  where argument_equals(:include_hidden, false)
end
```

## Data Layers

```elixir
use Ash.Resource,
  domain: MyApp.Blog,
  data_layer: AshPostgres.DataLayer  # or :embedded, Ash.DataLayer.Ets

postgres do
  table "posts"
  repo MyApp.Repo
end
```

## Migrations

Run `mix ash.codegen <name>` after modifying resources. Use `--dev` during development, final name at end.

## Testing

- Test through code interfaces
- Use `authorize?: false` when auth isn't the focus
- Use `Ash.can?` to test policies
- Use raising `!` functions

**Prevent deadlocks** - use unique values for identity fields in concurrent tests:

```elixir
%{email: "test-#{System.unique_integer([:positive])}@example.com"}
```

## References

- **Authorization & Policies**: See [references/policies.md](references/policies.md)
- **Relationships**: See [references/relationships.md](references/relationships.md)
- **Calculations & Aggregates**: See [references/calculations.md](references/calculations.md)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.