2
votes

I am working with simple phoenix elixir application. In this app I'm trying to expose a rest api to save and retrieve data.

In my case, if the primary key (email address ) is a duplicate, my application throws an error. I need to know the best way to handle this error; either try-catch or any other better solutions.

this is my data save method

def post(conn, %{"stooge" => stooge, "name" => name, "email" => email , "password" => password})do
    respond = Repo.insert!(%ApiDb.User{name: name, email: email, stooge: stooge, password: password})
    json conn, respond
  end

sample payload

{
    "stooge": "moe",
    "name": "Joe",
    "email": "[email protected]",
    "password":"asdasd"
}

models/user.ex

defmodule ApiDb.User do
  use ApiDb.Web, :model
  schema "users" do
    field :password, :string
    field :name, :string
    field :email, :string
    field :stooge, :string
    timestamps()
  end

   @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :email, :password, :stooge])
    |> validate_required([:name, :email, :password])
    |> validate_format(:email, ~r/@/)
  end
end

error enter image description here

I tried the try-catch blocks but there was no luck

try do
    respond = Repo.insert!(%ApiDb.User{name: name, email: email, stooge: stooge, password: password})
    json conn, respond
rescue 
  e in Ecto.ConstraintError -> IO.puts("An error occurred: ")
end
3
You should create a changeset function in ApiDb.User, add unique_constraint to it as the error message says, and then use Repo.insert in the controller with case to handle the error case. Try generating a model with phoenix.gen.json in a brand new app and study its source or read phoenixframework.org/docs/ecto-models (especially case Repo.insert(changeset) do ... on that page).Dogbert
i have this model defmodule ApiDb.User do use ApiDb.Web, :model schema "users" do field :password, :string field :name, :string field :email, :string field :stooge, :string timestamps() end endsmartechno
@Dogbert can you guide me with example to my appsmartechno

3 Answers

4
votes

Call the ApiDbUser.changeset function, then call Repo.insert with the changeset. It will return tuple with either :ok, or :error

def post(conn, params) do
  changeset = ApiDb.User.changeset(%ApiDb.User{}, params)
  case Repo.insert(changeset) do
    {:ok, user} -> 
       # create a render("user.json", params) function in your UserView
       render conn, "user.json", user: user
    {:error, changeset} 
      # create a render("error.json", params) function in your UserView
      render conn, "error.json", changeset: changeset
  end
end
2
votes

After Phoenix 1.3 release there is another way: to user action_fallback Here is an example from the docs

defmodule MyController do
  use Phoenix.Controller

  action_fallback MyFallbackController

  def show(conn, %{"id" => id}, current_user) do
    with {:ok, post} <- Blog.fetch_post(id),
         :ok <- Authorizer.authorize(current_user, :view, post) do

      render(conn, "show.json", post: post)
    end
  end
end

It allows to move error handling to fallback controller and reuse it across the app:

defmodule MyFallbackController do
  use Phoenix.Controller

  def call(conn, {:error, :not_found}) do
    conn
    |> put_status(:not_found)
    |> render(MyErrorView, :"404")
  end

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(403)
    |> render(MyErrorView, :"403")
  end
end
0
votes

found nice and easy way to do that

 def post(conn, %{"stooge" => stooge, "name" => name, "email" => email , "password" => password})do
    try do
        respond = Repo.insert!(%ApiDb.User{name: name, email: email, stooge: stooge, password: password})
        json conn, respond
    rescue 
      e in Ecto.ConstraintError ->  json conn, (%{"status" => false, "message" => e.message})
    end
  end