How to stop 500 errors when UUID doesn't match

Maybe You can test like this

iex> Ecto.UUID.cast "a863228-727c-4cf3-93f5-9b2f79df1288"
:error
iex> Ecto.UUID.cast Ecto.UUID.bingenerate()              
{:ok, "767f998a-424b-4270-beff-742b93f636fc"}
4 Likes

It also depends on the properties you want your program to have. In this specific case I would prefer to return an error to the client saying that the UUID specified is invalid.

3 Likes

To add to @Ankhers’s advice, @shahryarjb you can add a plug which would transform this particular ecto error into an http response which is more appropriate for your use case.

See Phoenix.NotAcceptableError - How can I return an error instead of having the app just have an exception?

defimpl Plug.Exception, for: Ecto.ChangeError do
  def status(_exception), do: 400
end
1 Like

You could replace your case with with.

with {:ok, _} <- Ecto.UUID.cast(cms_post_category_id),
  {:ok, _post} <- PostQuery.insert_post(allreq) 
do
  ...
end
2 Likes

Please see my suggestion code in my Phoenix Controller :

   def create_cms_post(conn, %{"title" => _title, "status" => _status, "post_type" => _post_type, "download_ext_link" => _download_ext_link, "price" => _price, "pic_x1_link" => _pic_x1_link, "pic_x2_link" => _pic_x2_link, "pic_x3_link" => _pic_x3_link, "group_acl" => _group_acl, "description" => _description, "changelog" => _changelog, "changelog_category" => _changelog_category, "plugin" => _plugin, "plugin_category" => _plugin_category, "discourse" => _discourse, "discourse_link" => _discourse_link, "screen_shot" => _screen_shot, "screen_shot_category" => _screen_shot_category, "learn" => _learn, "learn_category" => _learn_category, "seo_tag" => _seo_tag, "seo_alias_link" => _seo_alias_link, "seo_words" => _seo_words, "seo_description" => _seo_description, "seo_language" => _seo_language, "seo_language_link" => _seo_language_link, "cms_post_category_id" => cms_post_category_id} = allreq) do
      create_post(conn, allreq, Ecto.UUID.cast(cms_post_category_id))
   end

   defp create_post(conn, _allreq, :error) do
      conn
      |> put_status(400)
      |> json(%{error_code: "400"}) 
   end

   defp create_post(conn, allreq, {:ok, _}) do
      create_cms_posts = case PostQuery.insert_post(allreq) do
            {:ok, _post} 		   -> 
               conn
               |> put_status(200)
               |> json(%{message: "The post has been created."})            
            {:error, _changeset}  ->
               conn
               |> put_status(403)
               |> json(%{error_code: "403"})
         end
      create_cms_posts     
   end

Does anyone have a suggestion whether my code is invalid or valid?

It cannot work because Ecto.UUID.cast returns {:ok, _uuid}

UPDATE: Sorry, I didn’t read well :slight_smile:

It seems to be ok, does this work?

1 Like

I tested this several times and I didn’t have any problem. I think it works

Thank all of you whom helped me.

In Ecto3 when you specify in your model the primary key you can use Ecto.UUID instead of :binary_id so that the Ecto casting works correctly:

@primary_key {:id, Ecto.UUID, autogenerate: true}
schema "post" do
  ...
end

This way you should get the regular Ecto validations before going to the DB which looks like is what you want: {"id":"is invalid"}

From the ecto documentation:

… (typically :id or :binary_id , but can be any type)

7 Likes

Thank you for this! So simple! It also works with belongs_to:

belongs_to :post, Post, type: Ecto.UUID

1 Like

Thanks!