2

I'm trying to deploy a react + express app, but locally npm start does not fetch the react build that is within the client folder.

I have already done a react build, so a build folder does exist within the client folder.

folder structure

enter image description here

full folder structure

enter image description here

main.js

import 'dotenv/config';
import cors from 'cors';
import express from 'express';
import logger from 'morgan';
import path from 'path';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import userRoute from './routes/users';
import imageRoute from './routes/images';
import passport from 'passport';
import session from 'express-session';
import './config/passport';
import knex from 'knex';
import config from './knexfile'
import KnexSessionStore from 'connect-session-knex';
const knexSession = KnexSessionStore(session);
const myKnex = knex(config.development);
const store = new knexSession({
  knex:myKnex,
  // tablename:'sessions'
})

const app = express();

app.use(cors({
  origin:process.env.ALLOW_ORIGIN,
  preflightContinue: false,
  credentials: true,
  allowedHeaders: 'X-Requested-With, Content-Type, Authorization',
  methods: 'GET, POST, PATCH, PUT, POST, DELETE, OPTIONS',
  exposedHeaders: ['Content-Length', 'X-Foo', 'X-Bar'],
}))


app.use(logger('dev'));
// For React Stuff if need be
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'build')));
app.use(cookieParser());
app.use(bodyParser.json());
// you need body parser urlencoded so passport will not give a Missing Credentials error
app.use(session({
  store: store, 
  saveUninitialized: false,
  resave:false,
  cookie: {   maxAge: 30 * 24 * 60 * 60 * 1000 },  // 30 days
  secret : process.env.JWT_SECRET,

}));

app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended:false})); 
app.get('/', (req, res) => {
  res.send('Hello World!');
});
app.use('/users', userRoute);
app.use('/images', imageRoute);
// app.use('/images', imageRoute);
// app.use('/comments', imageRoute);
app.use(() => (req, res, next)  =>{
  res.locals.user = req.user; // This is the important line
  // req.session.user = user
  console.log(res.locals.user);
  next();
});

app.use('/', function (req, res, next) {
  var n = req.session.views || 0
  req.session.views = ++n
  res.end(n + ' views')
  console.log(n);
})

//build mode
app.use(express.static(path.join(__dirname, 'client/build')));
// if(process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, 'client/build')));
  //
  app.get('*', (req, res) => {
    res.sendfile(path.join(__dirname = 'client/build/index.html'));
  })
// }
//build mode
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname+'/client/public/index.html'));
})


// module.parent prevents the 
// Node / Express: EADDRINUSE, Address already in use error when unit testing
if(!module.parent){
  app.listen(process.env.PORT, () =>
    console.log(`Example app listening on port ${process.env.PORT}!`),
  );
 }

export default app;

package.json

{
  "name": "somethingapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "client-install": "npm install --prefix client",
    "test": "./node_modules/.bin/mocha --watch --require @babel/register",
    "server": "nodemon --exec babel-node main.js",
    "client": "cd ./client && npm start ",
    "start": "babel-node main.js",
    "startdev": "concurrently --kill-others  \"npm run client\" \"npm run server\" ",
    "migrate": "babel-node node_modules/.bin/knex migrate:latest",
    "rollback": "babel-node node_modules/.bin/knex migrate:rollback ",
    "seed": "babel-node node_modules/.bin/knex seed:run",
    "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client",
    "build": "concurrently \"cd client && npm run build\" \"npm build \""
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^3.0.6",
    "body-parser": "^1.19.0",
    "bookshelf": "^0.14.2",
    "bookshelf-validate": "^2.0.3",
    "chai-http": "^4.3.0",
    "cloudinary": "^1.14.0",
    "concurrently": "^4.1.0",
    "connect-multiparty": "^2.2.0",
    "connect-session-knex": "^1.4.0",
    "cookie-parser": "^1.4.4",
    "cors": "^2.8.5",
    "dotenv": "^8.0.0",
    "dump-die": "^1.0.0",
    "express": "^4.17.0",
    "express-session": "^1.16.1",
    "express-validator": "^5.3.1",
    "jsonwebtoken": "^8.5.1",
    "knex": "^0.16.5",
    "morgan": "^1.9.1",
    "multer": "^1.4.1",
    "multiparty": "^4.2.1",
    "passport": "^0.4.0",
    "passport-github": "^1.1.0",
    "passport-google-oauth": "^2.0.0",
    "passport-google-oauth20": "^2.0.0",
    "passport-jwt": "^4.0.0",
    "passport-local": "^1.0.0",
    "path": "^0.12.7",
    "pg": "^7.11.0",
    "validator": "^11.0.0",
    "var_dump": "^1.0.5"
  },
  "devDependencies": {
    "@babel/cli": "^7.4.4",
    "@babel/core": "^7.4.5",
    "@babel/node": "^7.4.5",
    "@babel/preset-env": "^7.4.5",
    "@babel/register": "^7.4.4",
    "chai": "^4.2.0",
    "mocha": "^6.1.4",
    "nodemon": "^1.19.0",
    "reify": "^0.19.1",
    "request": "^2.88.0"
  }
}

client/package.json

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.0.1",
    "axios": "^0.18.0",
    "jsonwebtoken": "^8.5.1",
    "jwt-decode": "^2.2.0",
    "moment": "^2.24.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-github-login": "^1.0.3",
    "react-google-login": "^5.0.4",
    "react-images-upload": "^1.2.6",
    "react-redux": "^7.0.3",
    "react-router-dom": "^5.0.0",
    "react-scripts": "3.0.1",
    "react-social-login-buttons": "^2.3.1",
    "react-thunk": "^1.0.0",
    "redux": "^4.0.1",
    "redux-thunk": "^2.3.0"
  },
  "scripts": {
    "start": "PORT=3001 react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "heroku-postbuild": "npm run build"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "enzyme": "^3.10.0",
    "enzyme-adapter-react-16": "^1.14.0",
    "enzyme-to-json": "^3.3.5",
    "redux-devtools-extension": "^2.13.8",
    "redux-mock-store": "^1.5.3"
  },
  "jest": {
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ]
  }
}
12
  • well you should define the full path. Where is main.js in the file structure? if its not inside /client then you need to tell it to look inside /client/build to get the build instead of static on the root Commented Jun 20, 2019 at 21:18
  • let me make an edit to show full folder structure Commented Jun 20, 2019 at 21:19
  • aka something like app.use(express.static(path.join(__dirname, 'client/build'))); or put your build folder at the root and not in /client Commented Jun 20, 2019 at 21:20
  • i made an edit that shows the full folder structure. Commented Jun 20, 2019 at 21:21
  • randal, you aren't pointing at the right location. if you console.log(path.join(__dirname, 'build')) in main.js you would see its missing the client folder in the path :) Commented Jun 20, 2019 at 21:23

1 Answer 1

2

It looks like you are trying to expose the build folder in a few different places in the code.

You should drop app.use(express.static(path.join(__dirname, 'build'))); as that path is invalid. Further down inside main.js you are doing the correct path, but your production if block is obsolete and can potentially mess up the paths again. So you can remove that as well.

Aka translate this

//build mode
app.use(express.static(path.join(__dirname, 'client/build')));
// if(process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, 'client/build')));
  //
  app.get('*', (req, res) => {
    res.sendfile(path.join(__dirname = 'client/build/index.html'));
  })
// }
//build mode
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname+'/client/public/index.html'));
})

to this

//build mode
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '/client/public/index.html'));
})
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the explanation John :).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.