Bump Your Deps

Update Backpex to the latest version:

defp deps do
  [
    {:backpex, "~> 0.18.0"}
  ]
end

The layout option in use Backpex.LiveResource stores the layout module reference in a module attribute, which creates a compile-time dependency. In most Phoenix applications this leads to a compile cycle:

YourLive  YourLayouts  YourWeb (:html)  YourRouter  YourLive

This causes a change in any of these files to recompile all of them, slowing down development.

To fix this, move the layout from the use option to the new layout/1 callback:

Before:

defmodule MyAppWeb.PostLive do
  use Backpex.LiveResource,
    layout: {MyAppWeb.Layouts, :admin},
    adapter_config: [...]
end

After:

defmodule MyAppWeb.PostLive do
  use Backpex.LiveResource,
    adapter_config: [...]

  @impl Backpex.LiveResource
  def layout(_assigns), do: {MyAppWeb.Layouts, :admin}
end

The layout option continues to work, so this change is not required. However, we recommend migrating to the callback to avoid compile cycles.

You can verify the improvement by running:

mix xref graph --format cycles --label compile-connected

Filter System Refactoring

The filter system has been refactored to use Ecto changeset-based validation. This is a breaking change that affects custom filters. See the Filter Validation Guide for full documentation.

Migration Checklist

  1. Add type/1 callback to all custom filters

    • Return :string for single-value filters
    • Return {:array, :string} for multi-select filters
    • Return :map for range filters (with start/end values)
    • Return :integer, :float, etc. for numeric filters
  2. Remove value parsing from query/4

    • Values are already cast to the correct type
    • Invalid values are filtered out before reaching query/4
  3. Optionally add changeset/3 for custom validation

  4. Optionally display validation errors in render_form/1

    • The @errors assign is now available (defaults to [])
    • Use the .error component for error display

No changes required if you're only using built-in filters with use Backpex.Filters.<Type>.

Before / After

# Before (v0.17) - query/4 received raw URL strings
def query(query, attribute, value, _assigns) do
  case Integer.parse(value) do
    {int_value, ""} -> where(query, [x], field(x, ^attribute) > ^int_value)
    _ -> query
  end
end

# After (v0.18) - query/4 receives validated, casted values
@impl Backpex.Filter
def type(_assigns), do: :integer

@impl Backpex.Filter
def query(query, attribute, value, _assigns) do
  where(query, [x], field(x, ^attribute) > ^value)
end

Adapter and Criteria Changes

If you're building a custom adapter or directly calling Backpex.Adapters.Ecto.apply_filters/4:

  • apply_filters now takes 4 arguments: (query, filter_values, filter_configs, assigns)
  • The criteria keyword list now uses filter_values and filter_configs keys instead of filters
  • filter_options/2 has been removed from Backpex.LiveResource — use @filter_values assign or LiveResource.build_criteria/1 instead
  • empty_filter_key/0 has been removed from Backpex.LiveResource

New Filter Validation Error Strings

The new filter validation introduces error messages that flow through Backpex.translate_error/1 and your configured error_translator_function. If you use Gettext for translation, add entries for these strings in the errors domain:

msgidUsed by
contains invalid optionsBoolean, MultiSelect filters
has invalid start valueRange filter
has invalid end valueRange filter
start must be less than or equal to endRange filter
has invalid date formatRange filter
is not a valid optionSelect filter