Why missing the :show route makes the :index route to fail in Phoenix?

I’m following Programming Phoenix 1.4 book.
In the rumbl app right at the start when we have to list all the users in the index.html template, we need the following two routes even if we are rendering only the index page.

get "/users", UserController, :index
get "/users/:id", UserController, :show

If we remove the second route, a request to localhost:4000/users fails. Why is so?
Why only the first route isn’t enough in that case?


If you want to reproduce this, follow the following,

  • Run,
mix phx.new rumbl

cd rumbl

mix ecto.create
  • remove everything below first first section tag from templates/page/index.html.eex

  • create lib/rumbl/accounts/user.ex with the following content

defmodule Rumbl.Accounts.User do
  defstruct [:id, :name, :username]
end
  • And lib/rumbl/accounts.ex with content,
defmodule Rumbl.Accounts do
  alias Rumbl.Accounts.User

  def list_users() do
    [
      %User{id: "1", name: "Jose", username: "josevalim"},
      %User{id: "2", name: "Bruce", username: "redrapids"},
      %User{id: "3", name: "Chris", username: "chrismccord"}
    ]
  end

  def get_user(id) do
    Enum.find(list_users(), fn map -> map.id == id end)
  end

  def get_user_by(params) do
    Enum.find(list_users(), fn map ->
      Enum.all?(params, fn {key, val} -> Map.get(map, key) == val end)
    end)
  end
end
  • Add this route to the router.ex
get "/users", UserController, :index

# notice that we're not adding the get "/users/:id" route
  • Add user_controller.ex as,
 defmodule RumblWeb.UserController do
  use RumblWeb, :controller

  alias Rumbl.Accounts

  def index(conn, _params) do
    users = Accounts.list_users()
    render conn, "index.html", users: users
  end
 end
  • And user_view.ex as,
defmodule RumblWeb.UserView do
  use RumblWeb, :view

  alias Rumbl.Accounts.User

  def first_name(%User{name: name}) do
    name
    |> String.split(" ")
    |> Enum.at(0)
  end
end
  • And finally the template lilb/rumbl_web/templates/user/index.html.eex,
<h1>Listing Users</h1>

<table>
  <%= for user <- @users do %>
    <tr>
      <td><b><%= first_name user %></b> (<%= user.id %>)</td>
      <td><%= link "View", to: Routes.user_path(@conn, :show, user.id) %></td>
    </tr>
  <% end %>
</table>

**Now when you run mix phx.server and access localhost:4000/users it will give you an error that you don’t have the :show route.

What is the point of adding the show route if the cycle is already complete?

Are you maybe trying to render a link to the details page on the index page? Something like Routes.user_path(@conn, :show, user.id)? If so, the route needs to be defined for that to work.

1 Like

Thank you for the reply! :heart:
I realized when I was editing the question, and as soon as I removed that line it worked.