Writing Custom Validations for Ecto Changesets

By Vikram Ramakrishnan

We often need to validate fields that are passed through a struct in Ecto
Changesets. Custom validations are very straightforward to write and can be
composed with one another easily. By using validate_change/3 which invokes a validator function, the function returns either a) the changeset or b) the changeset with errors if the validation fails.

Let’s take a look at a changeset with a custom validation validate_from_s3_bucket:

@our_url "https://our-bucket.s3.amazon.com"
def changeset(struct, params \\ %{}) do
struct
|> cast(params, @required_fields)
|> validate_required(@required_fields)
|> validate_from_s3_bucket(:url)
end
def validate_from_s3_bucket(changeset, field, options \\ []) do
validate_change(changeset, field, fn _, url ->
case String.starts_with?(url, @our_url) do
true -> []
false -> [{field, options[:message] || "Unexpected URL"}]
end
end)
end
validate_change/3: https://hexdocs.pm/ecto/Ecto.Changeset.html#validate_change/3

Here, we simply pass a new function validate_from_s3_bucket/2 to our changeset, which returns the changeset with no changes or with errors added. We can just as easily compose validations on top of one another in the form of validation functions that get piped to each other, which gives us our code a great deal of readability and flexibility.

Interested in discussing custom software needs more broadly? Drop me a line at vikram@quantlayer.com — I would love to chat with you. Follow us on Twitter at https://twitter.com/@QuantLayer