In this article, we will create a simple React app, run npm, run build, and serve the build folder from Express.
The first thing is to create a new folder and cd into that folder then run the usual
npx create-react-app frontend. This is to create a react app and name the folder frontend. Cd into the frontend folder and run
npm build. Once the command is done executing you will get a build folder. This is the folder we will serve from Express.
Cd back into the root folder. This is where we initialise the Express app. Run
npm init -y, and
npm i express. This would install express in the root folder. After installation is complete, create a file named index.js. Here in this folder require the path module(a module provided to us by Node.js). Now set-up an Express server as usual.
This is what your folder structure and app should look like.
The next step is to make the build folder that was created by npm run build static. To do this you need to call express.static() and pass in the path(absolute path) to express.static(). And express.static() has to be called in app.use() as a middleware. The question now becomes how do we get the absolute path to the build folder. The easiest way to do this is to make use of the path module we brought in earlier.
If you console.log(__dirname), you will get the absolute path to the root directory but we need to make the build folder in the frontend folder static. This is where the path module comes in. path.join construct paths for us easily. We call path.join() as path.join(__dirname, 'frontend', 'build'). This would return the absolute path to the build folder. So at the end, we should have;
app.use(express.static(path.join(__dirname, 'frontend', 'build')))
You can go ahead and
console.log(path.join(\__dirname, 'frontend', 'build')) to make what path.join() does clearer.
Now we create a route in Express. The route should be app.get(, callback). The wildcard() here means this route is triggered once you send a get request to any route at all.
Normally, this (*)route is usually placed after all other routes because of the way express works(looking for the first route that matches the URL you're sending the request to), this gives you the opportunity to define other routes like /api/, so that you will not always have the react app served once any get request is sent. Because in reality you will want to have other get request you will be sending to that Express API apart from serving the React app.
Now to the callback, we will pass to app.get(*, callback). In the callback, we want to send the HTML file that is in that build folder. Lucky for us express provides a way to send HTML files from the response object (the second argument passed into that callback). So we just call res.sendFile(). And now again we have to provide the absolute path to that HTML file. Just like we did before we call path.join but with an extra argument 'index.html'. path.join(__dirname, 'frontend', 'build', 'index.html'). In the end we have:
res.sendFile(path.join(__dirname, 'frontend', 'build', 'index.html'))
This is what you should have at the end.
Note: I created an app.get('/api') to show you can also send other get requests to other routes as long as the route serving the React app does not come before any of those routes.
I also have made a video explaining this same thing, In the video, I also show how you would deploy this application on Heroku(A PAAS). The video is given below: