I'm builing a MERN application with create-react-app (so no custom webpack) and a node server. I'm using nodemon to restart changes on the backend, and the problem is that about half of the time it seems that my front-end tries to render before nodemon can restart the node server, resulting in a ECONNREFUSED error.
I can solve the issue my simply refreshing the page, but it's annoying to have to do this repeatedly and I'd like to just figure out what the issue may be. This isn't happening if I run node server instead of nodemon.
Here's the relevant part of my client side package.json:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:7777"
and the server side package.json:
"scripts": {
"client-install": "npm intall --prefix client",
"start": "node server.js",
"server": "nodemon server.js",
"client": "cd client && npm start",
"dev": "concurrently \"npm run server\" \"npm run client\""
}
and my server.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const path = require('path');
const routes = require('./routes/index');
require('dotenv').config();
const app = express();
app.use(bodyParser.json());
mongoose.connect(process.env.DATABASE, {useNewUrlParser: true})
.then(() => console.log('MongoDb connected'))
.catch(err => console.log(`Mongo error ${err}`))
const port = process.env.PORT || 7777;
app.use('/', routes);
if (process.env.NODE_ENV === 'production') {
// Serve any static files
app.use(express.static(path.join(__dirname, 'client/build')));
// Handle React routing, return all requests to React app
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
}
app.listen(port, () => {
console.log(`Connected at port ${port}`)
})
I'm using axios for my front-end HTTP requests:
import axios from 'axios';
import FormData from 'form-data'
import keys from '../keys';
export const getPosts = () => {
return axios.get('/api')
}
export const post = (file, bodyInfo) => {
let formData = new FormData();
formData.append('file', file[0], file[0].name);
formData.append('bodyInfo', JSON.stringify(bodyInfo));
return axios.post('/api', formData, {
headers: {
'Content-Type': `multipart/form-data;
boundary=${formData._boundary}`,
}
})
}
export const getSinglePhoto = (id) => {
return axios.get(`/api/${id}`);
}
export const postUser = (userDetails) => {
console.log(userDetails);
return axios.post('/api/user', userDetails)
}
export const getUser = () => {
return axios.get('/user');
}
export const removeUser = (id) => {
return axios.delete(`/user/${id}`)
}
and here are my routes:
router.get('/api', postController.getPosts);
router.post('/api',
postController.type,
postController.uppic,
postController.cloudinary
);
router.get('/api/:id', postController.getSingle);
router.get('/user', userController.getUser);
router.post('/api/user', userController.postUser);
router.delete('/user/:id', userController.removeUser);
Try using CORS
instead of a package.json
proxy. I remember having random/intermittent connection issues when I used one. In short: Front-end runs on port 3000
and express
API runs on 5000
. When compiled, both run on 5000
and express
serves the compiled front-end js and acts like an API.
Very similar set up, but no connection problems:
express server package.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "NODE_ENV=production node app.js",
"server": "NODE_ENV=development nodemon app.js",
"client": "npm run start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\"",
"seeds": "NODE_ENV=development node seeds.js"
},
...
client/package.json (using a sass compiler, which you can use/ignore).
...
"scripts": {
"build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"start": "npm-run-all -p watch-css start-js",
"build": "npm run build-css && react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
...
client/src/actions/axiosConfig.js (then I create an axios
config to automatically point to my Express API running on 5000
)
import axios from 'axios';
export const app = axios.create({
baseURL: 'http://localhost:5000/api/',
withCredentials: true
})
client/src/actions/authActions.js (then import the axios
config)
import { app } from './axiosConfig';
const signinUser = props => dispatch => (
app.post(`signin`, { ...props })
.then(({data}) => {
dispatch({ type: types.SET_SIGNEDIN_USER, payload: data })
dispatch(fetchAvatarOnLogin());
})
.catch(err => dispatch({ type: types.SERVER_ERROR, payload: err }))
);
express server.js (I use consign
to import all the files):
const express = require('express');
const app = express();
const consign = require('consign');
consign({ locale: 'en-us', verbose: false})
.include('libs/middlewares.js')
.then("database")
.then("shared")
.then("services")
.then("controllers")
.then("routes")
.then('libs/server.js')
.into(app);
However, the equivalent would be:
// APP REQUIRED IMPORTS
const express = require('express');
const app = express();
...etc
// APP MIDDLEWARES
...
app.use(cors({credentials: true, origin: http://localhost:3000})) // allows receiving of cookies from front-end
app.use(morgan('tiny')); // logging framework
app.use(bodyParser.json()); // parses header requests (req.body)
app.use(bodyParser.urlencoded({ extended: true })); // allows objects and arrays to be URL-encoded
...etc
// DATABASE CONFIG/CONN
// APP SHARED FUNCS
// APP SERVICES (passport, sendgrid mailer, ...etc)
// APP CONTROLLERS
...
signin: (req, res, done) => passport.authenticate('local-login', err => (
(err || !req.session) ? sendError(err || badCredentials, res, done) : res.status(201).json({ ...req.session }))
)(req, res, done)
...etc
// APP ROUTES
...
app.post('/api/signin', signin);
...etc
// EXPRESS SERVER
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
app.get('*', (req, res) => res.sendFile(path.resolve('client', 'build', 'index.html')));
}
app.listen(5000);