In my previous post, I wrote:
I recently had to deal with camelCase-formated JSON while working on REST payloads. I am using an embedded schema and
Ecto.Changeset
to standardize the params map into an internal struct. However, I prefer to use the language snake_case convention for fields.
This is a perfect example of how amazing Ecto.Schema
and Ecto.Changeset
are; not
only to interact with a data store!
Let’s say we have the following JSON body to handle:
{
"email": "john.dow@email.com",
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": null
}
Plug.Parsers.JSON
will automatically convert the content into a map. And asumming the
Plug
from my previous post is in place, we have the following map with snake_case
keys:
%{
"email" => "john.dow@email.com",
"first_name" => "John",
"last_name" => "Doe",
"date_of_birth" => null
}
However, my context is expecting a struct!
defmodule Foo.Accounts.UserRequest do
defstruct email: nil, first_name: nil, last_name: nil, date_of_birth: nil
end
Thus I have to manually convert the "generic" map to an internal struct…
iex(1)> params = %{"email" => "john.doe@email.com", "first_name" => "John", "last_name" => "Doe"}
iex(2)> %UserRequest{
...(2)> email: params["email"],
...(2)> first_name: params["first_name"],
...(2)> last_name: params["last_name"],
...(2)> date_of_birth: params["date_of_birth"]
...(2)> }
%Foo.Accounts.UserRequest{
date_of_birth: nil,
email: "john.doe@email.com",
first_name: "John",
last_name: "Doe"
}
Ecto.Schema
and Ecto.Changeset
…Let’s rewrite the struct using embedded_schema/1
and leverage cast/3
to handle
the conversion!
defmodule Foo.Accounts.UserRequest do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :email, :string
field :first_name, :string
field :last_name, :string
field :date_of_birth, :date
end
def parse(params) do
%__MODULE__{}
|> cast(params, ~w(email first_name last_name date_of_birth)a)
|> validate_required(~w(email first_name last_name)a)
|> apply_changes()
end
end
Which gives us a solid internal structure, validation and error handling if needed!
Parsing the params map into a struct becomes as simple as:
iex(3)> UserRequest.parse(params)
%Foo.Accounts.UserRequest{
date_of_birth: nil,
email: "john.doe@email.com",
first_name: "John",
last_name: "Doe"
}
Happy coding 🛠