2

Phoenix seems to have an unconventional way of serializing JSON (it's new to me anyway). For example, let's say I have a table with two columns - id, and name. In nearly every other web framework the response to the show endpoint would return JSON that looks like this:

{
    "id": 1,
    "name": "foo"
}

Using the Phoenix generators the response looks like this:

{
    "data": 
    {
        "id": 1,
        "name": "foo"
    }
}

I'm seeing similar issues with POSTs, I need to post {"company": { "name": "foo" } } instead of { "name": "foo" }.

I have two questions:

  1. Is there an advantage to serializing JSON this way that I'm unaware of?
  2. Is there a way to handle JSON serialization in the "normal" way as described above?
2
  • With your way, how do you represent errors in the responses? Commented Sep 9, 2017 at 19:48
  • 1
    Clients primarily use HTTP status codes. For further detail they can look at the response body, typically this is JSON. Commented Sep 9, 2017 at 20:13

2 Answers 2

2
  1. I think that comes down to personal preference.

  2. You can change it of course.

In the generated view, change it from

def render("show.json", %{id: id, name: name}) do
  %{
    data: %{
      id: id,
      name: name
    }
  }
end

to

def render("show.json", %{id: id, name: name}) do
  %{
    id: id,
    name: name
  }
end

And in your controller function which accepts JSON from:

def create(conn, %{"company" => %{"name" => name}}) do
  ...
end

to

def create(conn, %{"name" => name}) do
  ...
end

Your generated functions might look different but I think you get the point.

Sign up to request clarification or add additional context in comments.

4 Comments

1. is not entirely true: With this approach, you will additionally have to define every column in your controller (create function), so it will be harder to maintain.
You don't have to pattern match. You can also pass the whole params map into your changeset and let it pick the keys it needs.
@Phillipp this got me very close. How do I handle the signature of the update controller method since id comes from the path and company_params is from the request body? It's currently def update(conn, %{"id" => id, "company" => company_params}).
@user140550 They should be mixed together, so you can pattern match on %{"id" => id, "name" => name} for example.
1

Posting diffs of Phoenix 1.3.0 generated code so that it might help others:

Controller changes

-  def create(conn, %{"company" => company_params}) do
+  def create(conn, company_params) do

-  def update(conn, %{"id" => id, "company" => company_params}) do
-    company = Repo.get!(Company, id)
+  def update(conn, company_params) do
+    company = Repo.get!(Company, conn.path_params["id"])

View changes

   def render("index.json", %{companies: companies}) do
-    %{data: render_many(companies, ConstructApi.CompanyView, "company.json")}
+    render_many(companies, ConstructApi.CompanyView, "company.json")
   end

   def render("show.json", %{company: company}) do
-    %{data: render_one(company, ConstructApi.CompanyView, "company.json")}
+    render_one(company, ConstructApi.CompanyView, "company.json")
   end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.