Hi fellow Elixirists!
About 8 months ago, I wrote a blog post on validating Phoenix controller parameters using Ecto Changesets in combination with Ecto Embedded Schemas (Using Ecto changesets for API request parameter validation).
Since then, I found that writing embedded schemas for validating parameters to be quite tedious. It required setting up new modules, a lot of boilerplate code, and complex file organisation.
So I decided to write a library that replaces the boilerplate code with a simple syntax, that can be written inside controllers, and is still based on Ecto Changesets to leverage its validation capabilities.
It’s called Goal, and it offers those capabilities with a nice syntax:
defmodule MyApp.SomeController do
import Goal
import Goal.Syntax
def create(conn, params) do
with {:ok, attrs} <- validate_params(params, schema()) do
...
end
end
defp schema do
defschema do
required :uuid, :string, format: :uuid
required :name, :string, min: 3, max: 3
optional :age, :integer, min: 0, max: 120
optional :gender, :enum, values: ["female", "male", "non-binary"]
optional :data, :map do
required :color, :string
optional :money, :decimal
optional :height, :float
end
end
end
end
It features:
- Compatibility with all primitive types from Ecto.Schema
- Customisable regexes for
email
,password
, andurl
formats, so you can maintain compatibility with your production system - Unlimited nested maps and lists of maps
-
trim
andsquish
to trim and collapse whitespaces -
optional
andrequired
arguments for defining optional and required fields - An extended version of
Ecto.Changeset.traverse_errors/2
(aptly calledGoal.Changeset.traverse_errors/2
) that works with Goal and Ecto’s existing functionality
You can use Goal for validating Phoenix controller action parameters, but it will work with any data source as long as it’s represented in a map.
I aim for Goal to cover all parameter validations that Ecto.Changeset
offers for database fields. I think I covered most with the 0.1.1
version; but if you’d like another validation, then please contribute or open an issue on GitHub.
It was inspired by Ruby’s dry-schema, and I had to borrow code from Ecto. Thank you for making such awesome libraries.
Feedback and suggestions are very welcome . For example, I was thinking about generating the chain of Ecto.Changeset functions at compile-time, to crunch out some additional
usecs