Serve React Build on separate server from Express/Node server

Viewed 17

I have a create-react-app where I split the client side to run on port localhost:3000 and my node js/express api side to run on port localhost:9000. I was using a Proxy at localhost:9000 to make my api requests from my client side to my backend. Now, I want to deploy this app. I have done npm run build on my client side and have that folder.

I was using the static server (serve -s build) at port 5000 to see it. However it is not getting any response or communicating with my express server. I don't even get any errors. I am certain the server is running but it seems they are not communicating. I also have CORS enabled.

Is this possible to run the build folder separately from my express server, have them running on two separate servers or I do I have to serve the static build from my express backend? In the end I'm going to use Apache but I'm trying to get the build up and running, communicating with my backend like how it was before I did the build. Please help!

[app.js(express/node js)]

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors');

var indexRouter = require('./routes/index');
var APIRouter = require('./routes/API');   // require route
var app = express();


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use("/API", APIRouter); // telling express to use this route but need to require it
app.use(function(req, res, next) {
  next(createError(404));
  var err = new Error("Not found from app.js")
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
  console.log(err);
});

module.exports = app;

[index.js(express/node js)]

var express = require('express');
var app = express();
var cors = require('cors');
var path = require('path');
var cookieParser = require('cookie-parser')
var secret = require('./config')[env]
var env = process.env.NODE_ENV || 'development';
var config = require('./config')[env];

const basicAuth = require('express-basic-auth');
const auth = basicAuth({
    users: {
      [config.adminCredentials.user]:config.adminCredentials.password
    }
});

app.use(cookieParser(config.adminCredentials.secret));
app.use(express.json());
app.use(cors());

app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  console.log("error:", err);
});


/* This is for deploying build folder , app is then hosted on port: 9000 */
// app.use(express.static(path.resolve(__dirname, '../../client/build')));
// //All other GET requests not handled before will return our React app
// app.get('/', (req, res) => {
//   res.sendFile(path.resolve(__dirname, '../../client/build', 'index.html'));
// }); 

app.get("/authenticate", auth, (req, res) => {
    const options = {
      httpOnly: true, //cookies are only accessible from a server
      signed:true,
      maxAge: 60 * 60 * 1000, // 1 hour
      sameSite:true // prevents the cookie from being sent in cross-site requests
    };
    
    if(req.auth.user === "*****") { // response reaches back to client if and only if the credentials sent along match with the request match
        res.cookie('name', '*****', options).send({screen:'******'});
    }
});

app.get('/read-cookie', (req, res) => {
  if(req.signedCookies.name === "*****") {
    res.send({screen:'*****'});
  }
  else {
    res.send({screen:'auth'});
  }
});

app.get('/clear-cookie', (req, res) => {
  res.clearCookie('name').end();
});

app.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

bin/www (express/ node)

#!/usr/bin/env node

/**
 * Module dependencies.
 */
var app = require('../app');
var debug = require('debug')('api:server');
var http = require('http');
const fs = require("fs");
const path=require('path');
var cookieParser = require('cookie-parser');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '9000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port, () => {
  console.log(`Server listening on ${port}`);
});
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

Client package.json

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.9.3",
    "@emotion/styled": "^11.9.3",
    "@fortawesome/fontawesome-svg-core": "^1.2.34",
    "@fortawesome/free-brands-svg-icons": "^5.15.2",
    "@fortawesome/free-regular-svg-icons": "^5.15.2",
    "@fortawesome/free-solid-svg-icons": "^5.15.2",
    "@fortawesome/react-fontawesome": "^0.1.14",
    "@mui/icons-material": "^5.8.4",
    "@mui/material": "^5.9.1",
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "axios": "^0.21.4",
    "cors": "^2.8.5",
    "node": "^17.4.0",
    "prop-types": "^15.7.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "3.4.3",
    "web-vitals": "^0.2.4"
  },
  "scripts": {
    "start": "HTTPS=true react-scripts --openssl-legacy-provider start",
    "build": "react-scripts --openssl-legacy-provider build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "proxy": "http://localhost:9000",
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

1 Answers

If you want to serve your react build from express server on different port then you can give this a try:

const express = require("express");
app.use(express.static(path.join(__dirname, "build")));
Related