Using BrowserRouter instead of HashRouter

A little context. I’m currently working on the frontend portion (actually just started) and ran into an interesting problem, regarding URL. I noticed when I’m using HashRouter which I’ve used in the pass for personal projects I’m getting localhost:4000/#/home which would look okay with a normal application but not when launching to production. Which comes in BrowserRouter. I was hoping to get some advice since I noticed when using the router, if I were to try going to the link directly or refresh the page, I’m getting a 404.

My route file looks like this

  pipeline :api do
    plug(:accepts, ["json"])
    plug(AmoramorWeb.Plugs.Context)
  end

  scope "/" do
    pipe_through(:api)

    forward("/api/v1", Absinthe.Plug, schema: Amoramor.Schema.Schema)

    if Mix.env() == :dev do
      forward(
        "/graphiql",
        Absinthe.Plug.GraphiQL,
        schema: Amoramor.Schema.Schema
      )
    end
  end

My BrowserRoute in the frontend.

  <Router>
    <Switch>
      <Route path="/" exact component={Landing} />
      <Route path="/sign-in" exact component={Signin} />
    </Switch>
  </Router>

I’ve looked into this article in https://stackoverflow.com/questions/27928372/react-router-urls-dont-work-when-refreshing-or-writing-manually/36623117#36623117 but I’m a bit confused. I’ve seen some application online where no modification that I can be found and it’s working (without crashing) and also seem to see answers where they suggest something like get("/*path", PageController, :index) to get passed the problem. All help, suggestion and how you may have solved this problem is welcome and appreciated. Thank you.

If I understand correctly what you are trying to do, the problem is that in your Phoenix router you miss a route for the root url.

You should add a route for / (or for any path, like the /*path that you mentioned) that serves the initial HTML/JS for your app. From that point on, the single-page frontend app will manage its own URLs, but the initial request has to be served by a server.

3 Likes

At the end of Phoenix router, You might add something like this (oh, as You both mentionned it :slight_smile: )

  scope "/", Api3dWeb do
    get "/*path", HomeController, :index
  end
1 Like

@lucaong @kokolegorille From my understanding it sounds like I’ll need to create a pointless controller which won’t be used except for the purpose of having the frontend hit some form of valid endpoint through HTTP, correct?

It’s not a pointless controller, in my case, the index template of this controller contains the root element, where the frontend will be launched.

In case of React, that would be where ReactDom render the React application.

As a side note, there is an alternative router, which uses hooks internally.

As it is a hook, this behaviour can be added to any functional compent.

If You use GraphQL and Relay modern, it is better to use Found router

2 Likes

I guess that is true although I’ve taken the Frontend portion out of the application and used plug CORSPlug to connect the frontend and the backend. I also really appreciate the other alternative approaches that I never knew existed. I will take a look at both of them :slight_smile: although I might end up mainly using the controller route. Been having trouble setting it up but I believe it’s because I’m not rendering an index file (an idea that came to mind as you mentioned template).

I won’t be using Relay. Currently the way I’m doing this is React -> Apollo Client -> GraphQL/Absinthe -> Elixit/Pheonix.

P.S. - Sorry for the wording before calling an approach of using the controller as a pointless one.

Thank you for helping me out in resolving this issue that has gotten me confused for some time.

I could have made it worthless by putting the root element in the layout :slight_smile:

BTW It’s probably better to separate both, but if You don’t, You will have benefit of livereload.

Actually, it’s weird :thinking: Everything is working and it started working once I deleted everything and set it back to how it was before this post…so confused lol