React Routes + NodeJS Routes
Tips and tricks with hosting a React Application (with react-router) within NodeJS (+ express).
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 ! 😃