Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Routing with an Express API and a ReactJS SPA

I recently deployed a site using React on Heroku. In the console of the browser I received an output of html text rather than my user interface javascript.

Link to my site Link to repository

I think that the issue is rooted in my server.js route to serve my index.html

server.js

// Allows us to place keys and sensitive info in hidden .env file
require("dotenv").config();

// Require Packages
const express = require("express");
const app = express();
const morgan = require("morgan")
const db = require("./models");
const routes = require("./routes");
const passport = require("passport");
const session = require("express-session")
const path = require("path");

const MySQLStore = require("express-mysql-session")(session);

require("./config/passport")(passport)
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

let options = {};
if (process.env.NODE_ENV === 'production') {
    options = {
        host: process.env.HOST,
        port: 3306,
        user: process.env.USER,
        password: process.env.PASSWORD,
        database: process.env.DB
    }
} else {
    options = {
        host: 'localhost',
        port: 3306,
        user: 'root',
        password: process.env.DB_PASSWORD,
        database: 'tracker'
    }
}


// Options for mysql session store

let sessionStore = new MySQLStore(options);

// Pass in mysql session store
app.use(session({
    key: 'surfing_dogs',
    secret: 'surfing_dogs',
    store: sessionStore,
    resave: false,
    saveUninitialized: false
}))

app.use(passport.initialize());
app.use(passport.session());

app.use(morgan('common'))


// THIS IS REALLY IMPORTANT FOR ROUTING CLIENT SIDE
// We want to have our app to use the build directory 
app.use(express.static(__dirname + '/client/build'))

// For every url request we send our index.html file to the route
app.get("/*", (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});

app.use(routes)


db.sequelize.sync({ force: false }).then(() => {
    let server = app.listen(process.env.PORT || 5000, function () {
        let port = server.address().port;
        console.log(`Server is listening on PORT ${port}`)
    })
})

To elaborate, I placed this route in my server.js because I have client-side routing and I want to trick the browser in always serving the index.html

Another reason that I want to solve this issue is because I am currently unable to login to my site using credentials.

Here is a small bit of the login process front-end wise. My goal was based on the login state, but unfortunately the login state is not changing.

 if (this.state.loggedIn === true) {
      return (
        <Router>
          <Navbar user={this.state.user} logout={this.logout} />
          <Switch>
            {this.state.userType === 'administrator' ? <Route path='/' exact component={() => <AdminHome user={this.state.user} />} /> : ''}
            {this.state.userType === 'instructor' ? <Route path='/' exact component={() => <InstructorHome user={this.state.user} />} /> : ''}

            {/* Path for student profile based on it */}
            <Route exact path='/student/:id' exact component={StudentProfile} />

            {this.state.userType === 'student' ? <Route path='/' exact component={() => <StudentHome user={this.state.user} />} /> : ''}
            <Route exact path='/settings' exact component={() => <Settings user={this.state.user} />} />
            <Route component={NoPage} />
          </Switch>
        </Router>
      )
    } else {
      return (
        <Router>
          <Switch>
            <Route path='/' exact component={SplashPage} />
            <Route path='/login' exact component={Login} />
            <Route path='/signup' exact component={Signup} />

            {/* Go to signup based on cohortID */}
            <Route path='/signup/:id' exact component={CohortSignup} />

            <Route component={NoPage} />

          </Switch>
        </Router>
      )
    }

Hope someone out there could help me out!

like image 804
pythonNovice Avatar asked Feb 27 '26 07:02

pythonNovice


1 Answers

Having looked at your repo for server.js you are sending all your traffic that hits your server (even your own api requests) to your front end.

First make sure your server-side routes start with something distinguishable such as

app.get('/api/*',(req,res)=>/*somecode*/)

This is because your server will confuse something like '/login' if it is also a route in your front end and will only end up serveing one or the other depending on when they are defined.

Then update your server.js to match this and it should work:

//API Requests handled first
require('./routes')(app);

//Non api requests in production
if (process.env.NODE_ENV === 'production') {
    app.use([someProductionMiddleware()])
    // Express will serve up production assets i.e. main.js
    app.use(express.static('client/build'));
    // If Express doesn't recognize route serve index.html
    const path = require('path');
    app.get('*', (req, res) => {
        res.sendFile(
            path.resolve(__dirname, 'client', 'build', 'index.html')
        );
    });
}
like image 105
Willman.Codes Avatar answered Mar 01 '26 20:03

Willman.Codes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!