How to Write Custom Validations for Ecto Changesets

Written for Phoenix 1.5.7 & Ecto 3.5.5

Ecto's changesets provide common validation options but you can also write your own. Below we have a basic module with a schema and changeset function for our tutorials table:

defmodule MyApp.Tutorials.Tutorial do
  use Ecto.Schema
  import Ecto.Changeset

  schema "tutorials" do
    field :name, :string
    field :is_useful, :boolean

    timestamps()
  end

  @doc false
  def changeset(tutorial, attrs) do
    tutorial
    |> cast(attrs, [:name, :is_useful])
    |> validate_required([:name, :is_useful])
  end
end

To add a custom validation, we'll first write the logic:

defmodule MyApp.Tutorials.Tutorial do

  # ...

  def validate_something(changeset) do
    case your_logic do
      true ->
        # Continue
        changeset
      false ->
        # Add an error to the changeset
        add_error(changeset, :some_key, "displayed error message")
    end
  end
end

Then we'll add the custom validation of the changeset function:

defmodule MyApp.Tutorials.Tutorial do

  # ...

  @doc false
  def changeset(tutorial, attrs) do
    tutorial
    |> cast(attrs, [:name, :is_useful])
    |> validate_required([:name, :is_useful])
    |> validate_something()
  end

  # ...

end

That's all there is to it! For further reading, check out the documentation for add_error and the default validation options that Ecto's changesets provide.