I have two models and associated database tables: User
and Address
. A user has_many
addresses and an address belongs_to
a user (so one of its required fields is user_id
). I am working on a user registration endpoint which requires a user to send over user details and address details in order to complete the registration process. The registration params comes over like this:
{
“user”: {
“email”: “admin@example.com”,
“password”: “password1”,
“phone_number”: “2122122121”
},
“shipping_address”: {
“city”: “Cambridge”,
“country”: “US”,
“postal_code”: “02139”,
“state_province”: “MA”,
“street_line1”: “111 Example St.”,
“street_line2”: “Suite 500”
}
}
I’m nesting a couple case statements and reject the registration if either the user or address fails to create:
def create(conn, %{"user" => user_params, "address" => address_params}) do
user_changeset = User.changeset(%User{}, user_params)
case Repo.insert(user_changeset) do
{:ok, user} ->
address_params = Map.merge(address_params, %{"user_id" => user.id})
address_changeset = Address.changeset(%Address{}, address_params)
case Repo.insert(address_changeset) do
{:ok, _address} ->
{:ok, jwt, _full_claims} = Guardian.encode_and_sign(user, :token)
conn
|> put_status(:created)
|> render(MyApp.SessionView, "create.json", jwt: jwt, user: user)
{:error, address_changeset} ->
Repo.delete(user)
conn
|> put_status(:unprocessable_entity)
|> render(MyApp.RegistrationView, "error.json", changeset: address_changeset)
end
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(MyApp.RegistrationView, "error.json", changeset: changeset)
end
end
This approach seems very flimsy to me (multiple database transactions, deleting the user if the shipping address fails, etc.). I would love to be able to wrap the whole registration process into a single transaction which can succeed or be rejected (and provide validation errors on failures). I came across Ecto.Multi, which seems like it might be a good fit here, but I’m not sure how to implement it for this use case. Any suggestions would be appreciated.