As useful as Ecto schemas are for databases, they are just as compelling for handling Form parameters. The key is using embedded_schema.

Define an embedded_schema that represents the request parameters with the types:

defmodule P do
  use Ecto.Schema
  embedded_schema do
    field :an_int, :integer
    field :a_date, :date
  end
end



Then use cast to get the parameters safely and with typing into a changeset:

iex(3)> p = cast( %P{}, %{ "an_int" => "4", "a_date" => "2019-04-04" }, [ :an_int, :a_date ] )
#Ecto.Changeset<
  action: nil,
  changes: %{a_date: ~D[2019-04-04], an_int: 4},
  errors: [],
  data: #P<>,
  valid?: true
>

If the changeset is valid, then you can use apply_changes to get the struct you wanted:

iex(5)> apply_changes(p)
%P{a_date: ~D[2019-04-04], an_int: 4, id: nil}

You can also use all the typical validators the Changeset offers.

If some of the data is bad/mistyped, the changeset will mark it as valid? false:

iex(6)> cast( %P{}, %{ "an_int" => "fred", "a_date" => "2019-04-04" }, [ :an_int, :a_date ] )
#Ecto.Changeset<
  action: nil,
  changes: %{a_date: ~D[2019-04-04]},
  errors: [an_int: {"is invalid", [type: :integer, validation: :cast]}],
  data: #P<>,
  valid?: false
>