Foreign key ID not showing in the JSON response for GET method

First of all, I am new to phoenix so I’m sorry if this question seems trivial.
I’m currently trying to create a chat app and I tried to create a REST API using mix phx.gen.json for schemas called users, chatrooms, and user_join_chat_rooms.
user_join_chat_room will have a foreign key field called user that will save the user id and another foreign key field called chat_room that will save the chat_room where that said user joined.

A problem that I have is after I created a user, a chat room, and then put them together in user_join_chat_room as foreign keys, if I were to get the user_join_chat_room data using the GET method, the only data that I get is the id of that user_join_chat_room, the foreign keys are not shown in the JSON response and I don’t know why.

I appreciate any help that I can get, thank you in advance.

Here are the schemas that I’ve got so far:

defmodule ChatApp.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :email, :string
    field :family_name, :string
    field :given_name, :string
    field :image_url, :string
    field :name, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:email, :name, :image_url, :given_name, :family_name])
    |> validate_required([:email, :name])
    |> unique_constraint(:email)
  end
end
defmodule ChatApp.ChatRoomManagement.ChatRoom do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @derive {Phoenix.Param, key: :id}
  schema "chatrooms" do
    field :name, :string

    timestamps()
  end

  @doc false
  def changeset(chat_room, attrs) do
    chat_room
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end
defmodule ChatApp.ChatRoomManagement.UserJoinChatRoom do
  use Ecto.Schema
  import Ecto.Changeset

  schema "user_join_chat_rooms" do
    field :user, :id
    field :chat_room, :binary_id

    timestamps()
  end

  @doc false
  def changeset(user_join_chat_room, attrs) do
    user_join_chat_room
    |> cast(attrs, [:user, :chat_room])
    |> validate_required([:user, :chat_room])
  end
end

Here are the migrations that I got:

defmodule ChatApp.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :email, :string
      add :name, :string
      add :image_url, :string
      add :given_name, :string
      add :family_name, :string

      timestamps()
    end

    create unique_index(:users, [:email])
  end
end
defmodule ChatApp.Repo.Migrations.CreateChatrooms do
  use Ecto.Migration

  def change do
    create table(:chatrooms, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :name, :string

      timestamps()
    end

  end
end
defmodule ChatApp.Repo.Migrations.CreateUserJoinChatRooms do
  use Ecto.Migration

  def change do
    create table(:user_join_chat_rooms) do
      add :user, references(:users, column: :id, type: :id, on_delete: :delete_all)
      add :chat_room, references(:chatrooms, column: :id, type: :binary_id, on_delete: :delete_all)

      timestamps()
    end

    create index(:user_join_chat_rooms, [:user])
    create index(:user_join_chat_rooms, [:chat_room])
  end
end

Here is my user_join_chat_room controller:

defmodule ChatAppWeb.UserJoinChatRoomController do
  use ChatAppWeb, :controller

  alias ChatApp.ChatRoomManagement
  alias ChatApp.ChatRoomManagement.UserJoinChatRoom

  action_fallback ChatAppWeb.FallbackController

  def index(conn, _params) do
    user_join_chat_rooms = ChatRoomManagement.list_user_join_chat_rooms()
    render(conn, "index.json", user_join_chat_rooms: user_join_chat_rooms)
  end

  def create(conn, %{"user_join_chat_room" => user_join_chat_room_params}) do
    with {:ok, %UserJoinChatRoom{} = user_join_chat_room} <- ChatRoomManagement.create_user_join_chat_room(user_join_chat_room_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.user_join_chat_room_path(conn, :show, user_join_chat_room))
      |> render("show.json", user_join_chat_room: user_join_chat_room)
    end
  end

  def show(conn, %{"id" => id}) do
    user_join_chat_room = ChatRoomManagement.get_user_join_chat_room!(id)
    render(conn, "show.json", user_join_chat_room: user_join_chat_room)
  end

  def update(conn, %{"id" => id, "user_join_chat_room" => user_join_chat_room_params}) do
    user_join_chat_room = ChatRoomManagement.get_user_join_chat_room!(id)

    with {:ok, %UserJoinChatRoom{} = user_join_chat_room} <- ChatRoomManagement.update_user_join_chat_room(user_join_chat_room, user_join_chat_room_params) do
      render(conn, "show.json", user_join_chat_room: user_join_chat_room)
    end
  end

  def delete(conn, %{"id" => id}) do
    user_join_chat_room = ChatRoomManagement.get_user_join_chat_room!(id)

    with {:ok, %UserJoinChatRoom{}} <- ChatRoomManagement.delete_user_join_chat_room(user_join_chat_room) do
      send_resp(conn, :no_content, "")
    end
  end
end

Here is the user_join_chat_room context:

defmodule ChatApp.ChatRoomManagement do
  @moduledoc """
  The ChatRoomManagement context.
  """

  import Ecto.Query, warn: false
  alias ChatApp.Repo

  alias ChatApp.ChatRoomManagement.ChatRoom

  @doc """
  Returns the list of chatrooms.

  ## Examples

      iex> list_chatrooms()
      [%ChatRoom{}, ...]

  """
  def list_chatrooms do
    Repo.all(ChatRoom)
  end

  @doc """
  Gets a single chat_room.

  Raises `Ecto.NoResultsError` if the Chat room does not exist.

  ## Examples

      iex> get_chat_room!(123)
      %ChatRoom{}

      iex> get_chat_room!(456)
      ** (Ecto.NoResultsError)

  """
  def get_chat_room!(id), do: Repo.get!(ChatRoom, id)

  @doc """
  Creates a chat_room.

  ## Examples

      iex> create_chat_room(%{field: value})
      {:ok, %ChatRoom{}}

      iex> create_chat_room(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_chat_room(attrs \\ %{}) do
    %ChatRoom{}
    |> ChatRoom.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Updates a chat_room.

  ## Examples

      iex> update_chat_room(chat_room, %{field: new_value})
      {:ok, %ChatRoom{}}

      iex> update_chat_room(chat_room, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_chat_room(%ChatRoom{} = chat_room, attrs) do
    chat_room
    |> ChatRoom.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a chat_room.

  ## Examples

      iex> delete_chat_room(chat_room)
      {:ok, %ChatRoom{}}

      iex> delete_chat_room(chat_room)
      {:error, %Ecto.Changeset{}}

  """
  def delete_chat_room(%ChatRoom{} = chat_room) do
    Repo.delete(chat_room)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking chat_room changes.

  ## Examples

      iex> change_chat_room(chat_room)
      %Ecto.Changeset{data: %ChatRoom{}}

  """
  def change_chat_room(%ChatRoom{} = chat_room, attrs \\ %{}) do
    ChatRoom.changeset(chat_room, attrs)
  end

  alias ChatApp.ChatRoomManagement.UserJoinChatRoom

  @doc """
  Returns the list of user_join_chat_rooms.

  ## Examples

      iex> list_user_join_chat_rooms()
      [%UserJoinChatRoom{}, ...]

  """
  def list_user_join_chat_rooms do
    Repo.all(UserJoinChatRoom)
  end

  @doc """
  Gets a single user_join_chat_room.

  Raises `Ecto.NoResultsError` if the User join chat room does not exist.

  ## Examples

      iex> get_user_join_chat_room!(123)
      %UserJoinChatRoom{}

      iex> get_user_join_chat_room!(456)
      ** (Ecto.NoResultsError)

  """
  def get_user_join_chat_room!(id), do: Repo.get!(UserJoinChatRoom, id)

  @doc """
  Creates a user_join_chat_room.

  ## Examples

      iex> create_user_join_chat_room(%{field: value})
      {:ok, %UserJoinChatRoom{}}

      iex> create_user_join_chat_room(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_user_join_chat_room(attrs \\ %{}) do
    %UserJoinChatRoom{}
    |> UserJoinChatRoom.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Updates a user_join_chat_room.

  ## Examples

      iex> update_user_join_chat_room(user_join_chat_room, %{field: new_value})
      {:ok, %UserJoinChatRoom{}}

      iex> update_user_join_chat_room(user_join_chat_room, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_user_join_chat_room(%UserJoinChatRoom{} = user_join_chat_room, attrs) do
    user_join_chat_room
    |> UserJoinChatRoom.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a user_join_chat_room.

  ## Examples

      iex> delete_user_join_chat_room(user_join_chat_room)
      {:ok, %UserJoinChatRoom{}}

      iex> delete_user_join_chat_room(user_join_chat_room)
      {:error, %Ecto.Changeset{}}

  """
  def delete_user_join_chat_room(%UserJoinChatRoom{} = user_join_chat_room) do
    Repo.delete(user_join_chat_room)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking user_join_chat_room changes.

  ## Examples

      iex> change_user_join_chat_room(user_join_chat_room)
      %Ecto.Changeset{data: %UserJoinChatRoom{}}

  """
  def change_user_join_chat_room(%UserJoinChatRoom{} = user_join_chat_room, attrs \\ %{}) do
    UserJoinChatRoom.changeset(user_join_chat_room, attrs)
  end
end
1 Like

You’ll get better help debugging your controller code if you post your controller code.

A general note about the schemas: the Ecto convention is to name foreign keys with _id so your columns would be user_id and chat_room_id, but they should still be appearing in the output JSON regardless of their names…

Thanks for the advice!
I’ve edited my post and added the controller and context codes.
I believe all the codes from the controller and context were generated by mix, so I can’t seem to understand why the IDs are not appearing in the JSON response.

1 Like

Found myself a solution after trying out a bunch of things.

I found out that the view (user_join_chat_room_view.ex) that was generated by mix is like this:

defmodule ChatAppWeb.UserJoinChatRoomView do
  use ChatAppWeb, :view
  alias ChatAppWeb.UserJoinChatRoomView

  def render("index.json", %{user_join_chat_rooms: user_join_chat_rooms}) do
    %{data: render_many(user_join_chat_rooms, UserJoinChatRoomView, "user_join_chat_room.json")}
  end

  def render("show.json", %{user_join_chat_room: user_join_chat_room}) do
    %{data: render_one(user_join_chat_room, UserJoinChatRoomView, "user_join_chat_room.json")}
  end

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

This is the reason why it only returned the id but not the foreign keys:

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

All I needed to do was to add the foreign key fields to it like so:

def render("user_join_chat_room.json", %{user_join_chat_room: user_join_chat_room}) do
    %{id: user_join_chat_room.id,
      user: user_join_chat_room.user,
      chat_room: user_join_chat_room.chat_room
    }
  end
2 Likes

I also got the same problem and now it’s fixed!! Thankyou! Very helpful :grin:

1 Like