Handling ReasonReact.Router and server side routing


#1

From this post a couples months ago, it seems many of us are using Node.js for our backend. I am curious: How are you guys serving your client-side files from your Node.js app?

I am using a catch-all route and express.static with absolute path to serve my static build files:

app.use(express.static(path.resolve(__dirname,  '..', 'build')));
app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'build', 'index.html'));
});

This has been working great, until I introduced URL params on my client-side routing like so:

  let routeFromUrl = (url: ReasonReact.Router.url) =>
    switch url.path {
    | [] => Home
    | ["about"] => About
    | ["books"] => Books
    | ["book", id] => Book /*  <--- url param */
    | _ => NotFound
  };

When I refresh on route for example /book/1, my app gets confused and does a GET request for a folder called "book" and file named "1". It’s unable to serve Index.js as you can see in the screenshot below.

Has anyone else run into similar issue? I would love to get any guidance or feedback. I am trying to avoid using hash string, as it is not a long-term solution.

I have duplicated this problem in this repo if anyone is interested taking a look.

Solution:
Serving Index.js on the mountpath “/*/Index.js” did the trick.

Like so:

app.use(express.static(path.resolve(__dirname,  '..', 'build')));
app.get('/*/Index.js', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'build', 'Index.js'));
});
app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'build', 'index.html'));
});

#2

I used express-spa middleware for this purpose:

So, when I access /book/1, Spa middleware returns my index.html, and express static returns src folder, which has index.js. Then, on client side, my reason router reads initial url and proceeds with its logic.


#3

You need to change “Index.js” to an absolute path from your domain’s root


#4

Thank you @gladimdim ! I wasn’t aware of express-spa middleware!


#5

I actually tried to do this before. Like this:

app.use(express.static(path.resolve(__dirname,  "..", "public")));
app.get("/Index.js", (req, res) => {
  res.sendFile(path.join(__dirname, "..", "public", "Index.js"));
});

It didn’t work. I just figured out why. The mountpath needs to be `"/book/Index.js". And that fixed it! :raised_hands:

Thanks @thangngoc89 !