React Routes + NodeJS Routes

Tips and tricks with hosting a React Application (with react-router) within NodeJS (+ express).

react — node — express — react-router

Introduction

This article revolves around the following areas to route things right.

  • create-react-app
  • react-router
  • express

One of the most common problems seen during the integration of React with NodeJS application is around routing. A common smell of problem can be related to hacking nginx with rewrite rules based on referer and other similar patterns.

create-react-app

A React application is most commonly built using create-react-app, which provides two modes of building an application — start and build.

start is commonly used for developing on the fly. While in this mode, there may be times when the underlying NodeJS routes may require to be accessed. This can be accessed using proxy attribute in package.json declared with-in the React application.

{
...
"proxy": "http://localhost:4000"
}

💡Note : Setting up proxy would resolve cors related issues while attempting to access NodeJS in development mode.

build takes care of creating a production ready build but implicitly assumes that the packaging unit is hosted in the server root. A most common message after the build might look like this.

If the application is going to be hosted onto a sub-directory (react assumes that the code would be deployed as root), then homepage attribute can be used to provide the absolute path of the hosted location inside package.json.

{
...
"homepage": "https://my-application.io/mysub-application1/"
}

Another option may be to use . as a value. This would enable the application to be hosted as a root or as a sub-directory.

💡Note: 404 errors on the hosted application is commonly related to homepage.

react-router

Some React applications may use react-router as a mechanism for client-side routing. In order for this routing to work correctly, one needs to setup the basename attribute of Router to handle this correctly.

<Router basename='/mysub-application1'.../>

💡Note: A symptom to detect basename problem is when the Router related components don’t render in the Browser.

The commonly used basename when running the application in start mode is / or not setting the basename.

An easier way to trick the start vs. build mode is by leveraging the following check.

process.env.NODE_ENV === 'production'

During the `build` target, the output would be written onto build directory in the same level as src location. Its required that, the contents of the build is copied onto public directory (a common convention used in a node application’s directory structure) in order to proceed into the express step.

express

express provides routing support in the server side to handle endpoints pertaining to services and/or static assets. The React application output is generally tagged as static asset.

import express from 'express'
import morgan from 'morgan'
import fs from 'fs'
import path from 'path'
const app = express()app.set('etag', 'strong') //browser caching of static assets should work properly//logging
const accessLogStream = fs.createWriteStream(config.access_file, { flags: 'a' })
app.use(morgan(':date[iso] :method :url :http-version :status ":referrer" ":user-agent" :remote-addr', {stream: accessLogStream})) //:method :url :response-time
//define all my services here
app.use('/ping', (req, res) => res.send("pong"))
//define the react-application here
app.use('/mysub-application1', express.static(path.resolve(__dirname, `../public`))) //optionally one can add some route handler to protect this resource?
app.get('/mysub-application1/*', (req,res) => { //this is required to support any client side routing written in react.
res.sendFile(path.join(__dirname, '../../public', 'index.html'))
})

One last step

Sometimes react route may be more than one level deep nesting of routes. During this time the page would fail to load.

This problem can only be seen thru hosted NodeJS. And not through start mode. Also, this problem would not be visible in build mode during react base route navigation.

The easy fix for it is to add base tag to head of react’s index.html

<head>
...
<base href="/mysub-application1"/>
...
</head>

The reason for base tag requirement is because, when ever a nested URL is being directed to NodeJS to load, it would diligently use the `/mysub-application1/*` as a match and would try to load index.html from the asked path (example if the asked path is `mysub-application1/foo/bar` then the static resources would try to resolve from `mysub-application1/foo/bar` instead of `mysub-application1`, which causes 404 issues).

⛑ Suggestions / Feedback ! 😃

Listener and reader