When trying to add a second controller, I get an error saying that the controller cannot be found

I’ve defined this controller:

defmodule IndieInfinityWeb.SearchController do
  use IndieInfinityWeb, :controller

  def index(conn, _params) do
  end

  def do_search(conn, %{"q" => q} = params) do
  end
end

This is the relevant part of my router:

scope "/", IndieInfinityWeb do
  pipe_through :browser

  get "/", PageController, :home
  get "/search", SearchController, :index
  post "/search", SearchController, :do_search
end

However, whenever I go to localhost:4000/search, I get function IndieInfinityWeb.IndieInfinityWeb.SearchController.init/1 is undefined (module IndieInfinityWeb.IndieInfinityWeb.SearchController is not available)

For some reason it’s trying to look for SearchController under IndieInfinityWeb.IndieInfinityWeb.

It seems like you likely have your posted router snippet nested under another scope "/", IndieInfinityWeb do. That would certainly do it. Not sure what else it could be as everything looks in order as you’ve posted it. If that’s not it then could you post your full router?

Here’s my entire router:

defmodule IndieInfinityWeb.Router do
  alias IndieInfinityWeb.SearchController
  use IndieInfinityWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {IndieInfinityWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", IndieInfinityWeb do
    pipe_through :browser

    get "/", PageController, :home
    get "/search", SearchController, :index
    post "/search", SearchController, :do_search
  end


  # Other scopes may use custom stacks.
  # scope "/api", IndieInfinityWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:indie_infinity, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: IndieInfinityWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end
end

The PageController which comes with the base generated application does work.

Ah! It’s the alias IndieInfinityWeb.SearchController.

This is going to make any place you type SearchController expand to IndieInfinityWeb.SearchController, ie, your scope will become:

  scope "/", IndieInfinityWeb do
    pipe_through :browser

    get "/", PageController, :home
    get "/search", IndieInfinityWeb.SearchController, :index
    post "/search", IndieInfinityWeb.SearchController, :do_search
  end

scope is a macro that automatically builds up module names.

1 Like

That might have been added by elixir-ls, which is a bit unfortunate. Using its autocomplete does - generally correctly - add the alias. But it’s not aware of the macro logic running in a router, which means module names are resolved using the prefixes from scopes.

Thanks! I didn’t even notice that (it might have been added automatically by elixir-ls as mentioned).