Handling ReasonReact.Router and server side routing


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.

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'));


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.


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


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


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 !