Packages
Integration library between Ash Framework and Backpex admin interface (early development). Provides a DSL for creating admin interfaces for Ash resources.
Current section
Files
Jump to
Current section
Files
ash_backpex
usage-rules.md
usage-rules.md
# AshBackpex Usage Rules
Rules for LLM agents working with AshBackpex - an integration library between Ash Framework and Backpex admin interfaces.
## Overview
AshBackpex provides a DSL for creating Backpex admin interfaces from Ash resources. It uses Spark DSL for compile-time code generation and automatically bridges Backpex operations to Ash actions.
## Creating a LiveResource
Always use `AshBackpex.LiveResource` with a `backpex` block:
```elixir
defmodule MyAppWeb.Admin.PostLive do
use AshBackpex.LiveResource
backpex do
resource MyApp.Blog.Post # Required: Ash resource module
layout {MyAppWeb.Layouts, :admin} # Required: LiveView layout
fields do
field :title
field :content
end
end
end
```
## Required Options
Every `backpex` block MUST have:
- `resource` - The Ash resource module
- `layout` - The LiveView layout as `{Module, :function}` tuple or function capture
## Field Configuration
### Basic Fields
Fields can reference attributes, relationships, calculations, or aggregates:
```elixir
fields do
field :title # Simple attribute
field :author # Relationship (auto-detects BelongsTo)
field :word_count # Calculation
field :comment_count # Aggregate
end
```
### Field Type Auto-Detection
AshBackpex automatically maps Ash types to Backpex fields:
- `Ash.Type.String` → `Backpex.Fields.Text`
- `Ash.Type.Boolean` → `Backpex.Fields.Boolean`
- `Ash.Type.Integer` / `Float` → `Backpex.Fields.Number`
- `Ash.Type.Date` → `Backpex.Fields.Date`
- `Ash.Type.DateTime` / `UtcDatetime` → `Backpex.Fields.DateTime`
- `:belongs_to` → `Backpex.Fields.BelongsTo`
- `:has_many` → `Backpex.Fields.HasMany`
- `:many_to_many` → `Backpex.Fields.HasMany`
- Atom with `one_of` constraint → `Backpex.Fields.Select`
- Array with `one_of` constraint → `Backpex.Fields.MultiSelect`
### Override Field Module
When auto-detection isn't sufficient, specify the module explicitly:
```elixir
field :content do
module Backpex.Fields.Textarea
end
```
### Relationship Fields
For relationships, specify `display_field` and optionally `live_resource`:
```elixir
field :author do
display_field :name # Field to display from related record
live_resource MyAppWeb.Admin.UserLive # Enables navigation links
end
```
Relationship fields derive Backpex `options_query` from Ash relationship
`filter`, `sort`, and `default_sort` settings. If a relationship only allows
records with `filter expr(type == :public)`, the generated options list will use
the same filter. Set `options_query` on the field to override this behavior.
For large `belongs_to` relationships, opt into a server-backed single-select
typeahead instead of loading every option:
```elixir
field :author do
display_field :name
typeahead true
typeahead_limit 10
debounce 300
prompt "Choose an author"
end
```
The typeahead searches `display_field`. The existing `debounce` option controls
search debouncing.
The dropdown initially shows up to `typeahead_limit` options from the normal
relationship query, then replaces them with matching results as the user types.
Relationship filters, sorts, read action, context, actor, tenant, and
authorization continue to flow through the field's derived `options_query`.
### Repeating Child Forms
`has_many` relationships continue to use the selection-oriented
`Backpex.Fields.HasMany` by default. Opt into repeated child forms with
`Backpex.Fields.InlineCRUD` and configure its child fields:
```elixir
field :rows do
module Backpex.Fields.InlineCRUD
except [:index]
child_fields do
field :title, Backpex.Fields.Text
field :position, Backpex.Fields.Number
end
end
```
AshBackpex derives `type: :assoc` for a `has_many`, adds move-up and move-down
controls, normalizes InlineCRUD's order and delete parameters to an ordered
list, and includes existing child primary keys in hidden inputs. The parent
create/update action must accept an `{:array, :map}` argument and connect it to
the relationship:
```elixir
argument :rows, {:array, :map}, allow_nil?: false, default: []
change manage_relationship(:rows, type: :direct_control)
```
Backpex does not support nesting an InlineCRUD child inside another InlineCRUD.
### Searchable Fields
Enable search on string fields:
```elixir
field :title do
searchable true
end
```
### Field Visibility
Control where fields appear:
```elixir
field :inserted_at do
only [:index, :show] # Only show on index and show views
end
field :internal_notes do
except [:index] # Hide from index view
end
```
## Preloading Relationships
Use `load` to preload relationships, calculations, or aggregates:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
load [:author, :comments, nested: [:author]]
fields do
field :author
end
end
```
## Filters
Add filters to the index view:
```elixir
filters do
filter :published do
module Backpex.Filters.Boolean
end
filter :status do
module Backpex.Filters.Select
label "Post Status" # Optional custom label
end
end
```
## Item Actions
Add or remove per-item actions:
```elixir
item_actions do
strip_default [:delete] # Remove default delete action
action :archive, MyApp.ItemActions.Archive # Add custom action
end
```
## Custom Ash Actions
Specify which Ash actions to use (defaults to primary actions):
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
create_action :admin_create
read_action :admin_read
update_action :admin_update
destroy_action :soft_delete
end
```
## Custom Changesets
Provide custom changeset functions for advanced control:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
create_changeset fn item, params, metadata ->
assigns = Keyword.get(metadata, :assigns)
Ash.Changeset.for_create(item.__struct__, :create, params,
actor: assigns.current_user
)
end
end
```
The changeset function receives:
- `item` - The struct being created/updated
- `params` - Form parameters
- `metadata` - Keyword list with `:assigns` and `:target` keys
## Display Names
Customize resource labels:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
singular_name "Blog Post"
plural_name "Blog Posts"
end
```
## Sorting
Set default sort order:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
init_order %{by: :inserted_at, direction: :desc}
end
```
## Pagination
Configure pagination options:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
per_page_default 25
per_page_options [10, 25, 50, 100]
end
```
## Form Panels
Organize form fields into panels:
```elixir
backpex do
resource MyApp.Blog.Post
layout {MyAppWeb.Layouts, :admin}
panels [
content: "Content",
settings: "Settings"
]
fields do
field :title do
panel :content
end
field :published do
panel :settings
end
end
end
```
## Authorization
AshBackpex automatically integrates with Ash authorization:
- Uses `assigns.current_user` as the actor
- Checks `Ash.can?/2` for CRUD operations
- Hides buttons/actions the user can't perform
Ensure your Ash resources have policies defined and `current_user` is set in assigns.
## Router Setup
Add routes for your LiveResource:
```elixir
scope "/admin", MyAppWeb.Admin do
pipe_through [:browser, :admin_auth]
live "/posts", PostLive
end
```
## Common Patterns
### Read-Only Admin
For resources without update/destroy:
```elixir
backpex do
resource MyApp.AuditLog
layout {MyAppWeb.Layouts, :admin}
item_actions do
strip_default [:edit, :delete]
end
fields do
field :action
field :user
field :inserted_at
end
end
```
### Rich Text Content
Use Textarea for longer content:
```elixir
field :content do
module Backpex.Fields.Textarea
rows 15
end
```
### Date Formatting
Custom date display format:
```elixir
field :published_at do
format "%B %d, %Y at %H:%M"
end
```
## Troubleshooting
### "Unable to derive Backpex.Field module"
The field type couldn't be auto-detected. Solutions:
1. Ensure the field name matches an attribute/relationship/calculation/aggregate on the resource
2. Specify the module explicitly: `field :foo do module Backpex.Fields.Text end`
### Authorization Issues
If actions are hidden unexpectedly:
1. Check that `current_user` is set in your LiveView assigns
2. Verify your Ash resource policies allow the action
3. Test with `Ash.can?({resource, action}, user)` in IEx
### Fields Not Loading
If relationship/calculation fields show errors:
1. Add them to the `load` option: `load [:author, :word_count]`
2. Ensure the field is defined on the Ash resource