reactjseconnrefused

ECONNREFUSED error for create-react-app and node server


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);

Solution

  • 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);