Skip to content

Getting Started with Phoenix

Jeffrey Gelens edited this page Jul 16, 2019 · 5 revisions

Getting started with JaSerializer in a Phoenix web application

This page shows you how to use the Phoenix JSON generator and update it to work with JaSerializer.

Use Phoenix generator to create schema, context, controller, view (serializer), and tests

mix phx.gen.json Blog Article articles body:text

Update controller to render "json-api" instead of "json"

lib/my_app_web/controllers/article_controller.ex

defmodule MyAppWeb.ArticleController do
  use MyAppWeb, :controller

  alias MyApp.Blog
  alias MyApp.Blog.Article

  action_fallback MyAppWeb.FallbackController

  def index(conn, _params) do
    articles = Blog.list_articles()
-    render(conn, "index.json", articles: articles)
+    render(conn, "index.json-api", data: articles)
  end

  def create(conn, %{"article" => article_params}) do
    with {:ok, %Article{} = article} <- Blog.create_article(article_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.article_path(conn, :show, article))
-      |> render("show.json", article: article)
+      |> render("show.json-api", data: article)
    end
  end

  def show(conn, %{"id" => id}) do
    article = Blog.get_article!(id)
-    render(conn, "show.json", article: article)
+    render(conn, "show.json-api", data: article)
  end

  def update(conn, %{"id" => id, "article" => article_params}) do
    article = Blog.get_article!(id)

    with {:ok, %Article{} = article} <- Blog.update_article(article, article_params) do
-      render(conn, "show.json", article: article)
+      render(conn, "show.json-api", data: article)
    end
  end

  def delete(conn, %{"id" => id}) do
    article = Blog.get_article!(id)

    with {:ok, %Article{}} <- Blog.delete_article(article) do
      send_resp(conn, :no_content, "")
    end
  end
end

Update view/serializer to use JaSerializer.PhoenixView and DSL

lib/my_app_web/views/article_view.ex

defmodule MyAppWeb.ArticleView do
  use MyAppWeb, :view
-  alias MyAppWeb.ArticleView
+  use JaSerializer.PhoenixView

-  def render("index.json", %{articles: articles}) do
-    %{data: render_many(articles, ArticleView, "article.json")}
-  end
-
-  def render("show.json", %{article: article}) do
-    %{data: render_one(article, ArticleView, "article.json")}
-  end
-
-  def render("article.json", %{article: article}) do
-    %{id: article.id,
-      body: article.body}
-  end
+  attributes [:body]
end

Update controller test conn headers

test/my_app_web/controllers/article_controller_test.exs

defmodule MyAppWeb.ArticleControllerTest do
  use MyAppWeb.ConnCase

  alias MyApp.Blog
  alias MyApp.Blog.Article

  @create_attrs %{
    body: "some body"
  }
  @update_attrs %{
    body: "some updated body"
  }
  @invalid_attrs %{body: nil}

  def fixture(:article) do
    {:ok, article} = Blog.create_article(@create_attrs)
    article
  end

  setup %{conn: conn} do
-    {:ok, conn: put_req_header(conn, "accept", "application/json")}
+    conn =
+      conn
+      |> put_req_header("accept", "application/vnd.api+json")
+      |> put_req_header("content-type", "application/vnd.api+json")
+
+   {:ok, conn: conn}
  end

  describe "index" do
    test "lists all articles", %{conn: conn} do
      conn = get(conn, Routes.article_path(conn, :index))
      assert json_response(conn, 200)["data"] == []
    end
  end

  describe "create article" do
    test "renders article when data is valid", %{conn: conn} do
-      conn = post(conn, Routes.article_path(conn, :create), article: @create_attrs)
+      conn =
+        post conn, Routes.article_path(conn, :create),
+        %{
+          "meta" => %{},
+          "data" => %{
+            "type" => "article",
+            "attributes" => @valid_attrs
+          }
+        }
+
      assert %{"id" => id} = json_response(conn, 201)["data"]

-      conn = get(conn, Routes.article_path(conn, :show, id))
-
-      assert %{
-               "id" => id,
-               "body" => "some body"
-             } = json_response(conn, 200)["data"]
+      assert Repo.get_by(Article, @valid_attrs)
    end

    test "renders errors when data is invalid", %{conn: conn} do
-      conn = post(conn, Routes.article_path(conn, :create), article: @invalid_attrs)
+      conn =
+        post conn, Routes.article_path(conn, :create),
+        %{
+          "meta" => %{},
+          "data" => %{
+            "type" => "article",
+            "attributes" => @invalid_attrs
+          }
+        }
+
      assert json_response(conn, 422)["errors"] != %{}
    end
  end

  describe "update article" do
    setup [:create_article]

    test "renders article when data is valid", %{conn: conn, article: %Article{id: id} = article} do
-      conn = put(conn, Routes.article_path(conn, :update, article), article: @update_attrs)
+      conn =
+        put conn, Routes.article_pat(conn, :update, article),
+        %{
+          "meta" => %{},
+          "data" => %{
+            "type" => "article",
+            "id" => article.id,
+            "attributes" => @update_attrs
+          }
+        }
+
      assert %{"id" => ^id} = json_response(conn, 200)["data"]

-      conn = get(conn, Routes.article_path(conn, :show, id))
-
-      assert %{
-               "id" => id,
-               "body" => "some updated body"
-             } = json_response(conn, 200)["data"]
+      assert Repo.get_by(Article, @valid_attrs)
    end

    test "renders errors when data is invalid", %{conn: conn, article: article} do
-      conn = put(conn, Routes.article_path(conn, :update, article), article: @invalid_attrs)
+      conn =
+        post conn, Routes.article_path(conn, :update, article),
+        %{
+          "meta" => %{},
+          "data" => %{
+            "type" => "article",
+            "attributes" => @invalid_attrs
+          }
+        }
+
      assert json_response(conn, 422)["errors"] != %{}
    end
  end

  describe "delete article" do
    setup [:create_article]

    test "deletes chosen article", %{conn: conn, article: article} do
      conn = delete(conn, Routes.article_path(conn, :delete, article))
      assert response(conn, 204)

      assert_error_sent 404, fn ->
        get(conn, Routes.article_path(conn, :show, article))
      end
    end
  end

  defp create_article(_) do
    article = fixture(:article)
    {:ok, article: article}
  end
end