0

File structure

.root
|-client
| -build
| -node_modules
| -public
| -src
|  -components
|  -resources
|   -data
|   -images
|   -templates
|-server
| -models
| -public
| -routes
| -server.js

server.js

//Required NPM Packages
const express                =         require('express'),
      app                    =         express(),
      cors                   =         require('cors'),
      bodyParser             =         require('body-parser'),
      mongoose               =         require('mongoose'),
      methodOverride         =         require('method-override');

//MongoDB models.
const Product                =         require('./models/Product');

//Routes.
const indexRoute             =         require('./routes/index');
const demoRoute              =         require('./routes/demo');
const bootstrapTemplateRoute =         require('./routes/bootstrapTemplate');

//Port.
const _PORT = process.env.PORT || 5000;

//Connect to mongoDB.
mongoose.connect({pathToMongoDB}, {useNewUrlParser:true, useUnifiedTopology:true});

//Setup body-parser.
app.use(bodyParser.urlencoded({extended:true}));

//Allow express/node to accept Cross-origin resource sharing.
app.use(cors());

//Set view engine to EJS.
app.set('view engine', 'ejs');

//Change views to specified directory
app.set('views', path.join(__dirname, '..', 'client','src','Resources','templates'));


app.use(express.static(__dirname+"/public"))
app.use(express.static("client/build"));
app.use(express.static("client/Resources/templates"));


//Setup method override.
app.use(methodOverride("_method"));

//register routes to express.
app.use('/', indexRoute);
app.use('/demo', demoRoute);
app.use('/bootstrapTemplate', bootstrapTemplateRoute)

//listen to established port.
app.listen(_PORT, () => {
    console.log(`The server has started on port ${_PORT}!`);
});


module.exports = app;

Question

When I click the back button or load the page in my browser bar nodeJS states that it cannot get the page. I recognise that the front-end and back-end request are different as the React-Router takes care of routing via it's own javaScript allowing for zero page reloads, but how do you actually solve the problem on nodeJS/Express?

Also when I go to localhost:3000/demo it returns the data from my mongoDB and renders it in json format rather then loading the correct page.

Currently working on a MERN Stack with the below Nginx basic routing config.

http{

    server{
        listen 3000;
        root pathToRoot/client/build;

        location / {
            proxy_pass http://localhost:5000/;
        }
            
    }
}

events {

}

I believe the problem is within my routing via express/nodejs. I can't create express specific routes and render the correct page because react is rendering it for me. I looked at the following question React-router urls don't work when refreshing or writing manually. Should I just do a catch-all and re-route back to the main index page? This seems like it would render bookmarks unusable.

Edit

Here are the 2 node routes.

index.js

const express                = require('express'),
      router                 = express.Router();


router.get("/", (req,res) => {
    //Render index page.
    res.render("index");
});

demo.js

 const express = require('express'),
      router = express.Router();

const Product = require('../models/Product');


router.get('/:searchInput', (req,res) => {
      Product.find({ $text: {$search: req.params.searchInput}}, (err,foundItem) => {
            if(err){
                  console.log(err);
            }else{
                  res.send(foundItem);
            }            
      })
})

router.get('/', (req, res) => {     
      Product.find({}, (err, foundItem) => {
            res.send(foundItem);
      });
});


module.exports = router;
rwright94
  • 45
  • 1
  • 7
  • [Does this work for you](https://stackoverflow.com/questions/43951720/react-router-and-nginx)? I think the issue is that you are only serving the `index.html` file for the `/` route, but you want to serve it for all routes. – Tholle Dec 23 '20 at 03:02
  • I added an edit with the two routes that I currently have. I'm currently only serving the index.html file when we land on the page. Although I'm confused that I would need to render more than one page as React renders all the front-end pages. When I route to the demo page the call is made to mongo and I get the data back. As per React useEffect/useState – rwright94 Dec 23 '20 at 03:17
  • I might be way off but generally the servers are set up to return index.html instead of a 404 page. That way, you can ask the server for any ol' route and if it doesn't know, it returns the React page and it React-Router DOM figures it out. – Will Dec 23 '20 at 05:44

1 Answers1

0

I would try separate out your development folders from your build folders as it can get a bit messy once you start running react build. A structure I use is:

api  build  frontend  run_build.sh

The api folder contains my development for express server, the frontend contains my development for react and the build is created from the run_build.sh script, it looks something like this.

#!/bin/bash
rm -rf build/

# Build the front end
cd frontend
npm run build

# Copy the API files
cd ..
rsync -av --progress api/ build/ --exclude node_modules

# Copy the front end build code
cp -a frontend/build/. build/client/

# Install dependencies
cd build
npm install

# Start the server
npm start

Now in your build directory you should have subfolder client which contains the built version of your react code without any clutter. To tell express to use certain routes for react, in the express server.js file add the following.

NOTE add your express api routes first before adding the react routes or it will not work.

// API Routing files - Located in the routes directory // 
var indexRouter         = require('./routes/index')
var usersRouter         = require('./routes/users');
var oAuthRouter         = require('./routes/oauth');
var loginVerification   = require('./routes/login_verification')

// API Routes //
app.use('/',indexRouter);
app.use('/users', usersRouter);
app.use('/oauth',oAuthRouter);
app.use('/login_verification',loginVerification);

// React routes - Located in the client directory // 
app.use(express.static(path.join(__dirname, 'client'))); // Serve react files
app.use('/login',express.static(path.join(__dirname, 'client')))
app.use('/welcome',express.static(path.join(__dirname, 'client')))

The react App function in the App.js component file will look like the following defining the routes you have just added told express to use for react.

function App() {
  return (
   <Router>
     <div className="App">
      <Switch>
        <Route exact path="/login" render={(props)=><Login/>}/>
        <Route exact path="/welcome" render={(props)=><Welcome/>}/>
      </Switch>
     </div>
   </Router> 
  );
}

Under the components folder there are components for login and welcome. Now navigating to http://MyWebsite/login will prompt express to use the react routes, while for example navigating to http://MyWebsite/login_verification will use the express API routes.

WK123
  • 620
  • 7
  • 18