4

An app I'm creating uses the MERN stack (MySql, Express, React, Node). The frontend and backend end are each stored in 2 separate Githubs repos and are hosted on 2 separate Heroku instances. Both the frontend and backend deploy to Heroku successfully.

The question is how do I get both the frontend and backend to communicate with each other? Should the user start at the frontend heroku which will call the backend or should they start at the backend?

I've tried setting the frontend Proxy address to the heroku link for the backend but apparently this Proxy address is only used in the development environment. I've tried adding a server file to front end which eliminates the "Invalid Host header" error but doesn't solve the communication issue.

Here is the frontend package.json:

{
  "name": "healthy-front-end",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "axios": "^0.19.0",
    "express": "^4.17.1",
    "materialize-css": "^1.0.0-rc.2",
    "moment": "^2.24.0",
    "react": "^16.8.6",
    "react-datepicker": "^2.8.0",
    "react-dom": "^16.8.6",
    "react-modal": "^3.9.1",
    "react-moment": "^0.9.2",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "dev": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "heroku-postbuild": "npm run build"
  },
  "proxy": "http://localhost:3001",
  "eslintConfig": {
    "extends": "react-app"
  }
}

Here is the backend server.js:

require('dotenv').config();

const express = require('express');
const app = express();
const path = require('path');
const env = process.env.NODE_ENV || 'development';
const reactConfig = require(path.join(__dirname, '/config/config.static.json'))[env];
const PORT = process.env.PORT || 3001;

// Define middleware here
app.use(express.static(path.join(__dirname, reactConfig))); // serving react files
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cors());

// Serve up static assets
if (process.env.NODE_ENV === 'production') {
  app.use(express.static('client/build'));
}

// Routes
require('./controllers/routes')(app);

// Start the API server
app.listen(PORT, () =>
  console.log(`  ==> API Server now listening on PORT ${PORT}!`)
);

Here is the config.static.json file with the front end url:

{
  "development": "client/public",
  "production": "https://healthy-people-front-end.herokuapp.com/"
}

Here is the config.js file with prod url added:

let config = {
    local: {
        mysql:{
            url: process.env.DB_URL,
        },
        apiKeys:{}
    },
    prod: {
        mysql:{
            url: process.env.JAWSDB_URL,
        },
        url: 'https://healthy-people-front-end.herokuapp.com/',
        apiKeys:{}
    }
};

module.exports = config[process.env.APP_ENV || 'local'];

Here is the backend package.json:

{
  "name": "healthy-back-end",
  "version": "1.0.0",
  "description": "API for HealthMate App",
  "main": "server.js",
  "scripts": {
    "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
    "start:prod": "node server.js",
    "start:dev": "concurrently \"nodemon --ignore 'client/*'\" \"npm run client\"",
    "dev": "concurrently \"npm start\" \"npm run client\"",
    "client": "cd ../HealthMate-frontend && npm run start"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "concurrently": "^4.1.2",
    "cors": "^2.8.5",
    "dotenv": "^8.1.0",
    "express": "^4.17.1",
    "hashpass": "0.0.0",
    "mysql": "^2.17.1",
    "nodemon": "^1.19.1",
    "uuid": "^3.3.2"
  }
}

How do I get both ends to communicate with each other? Any help is greatly appreciated.

Daniel S.
  • 113
  • 1
  • 12
  • If they are used in separate Heroku they most probably doesn't share same localhost. You should deploy backend to heroku then in some env variable pass the url of backend service to frontend service. – Maciej Trojniarz Sep 03 '19 at 08:10
  • @MaciejTrojniarz thank you. Isn't the localhost only used for local development? Good idea, I'll look for examples for how to include a URL in the .env file. – Daniel S. Sep 03 '19 at 19:36
  • Or I would actually do the following: release react bundle as some minified js/css resources (to any of the services CDN/S3/Your backend service) and then I the backend code serve the Index.html file which will point to those files then you could use the same path for frontend and api which will remove the need for passing url to backend. – Maciej Trojniarz Sep 03 '19 at 20:16
  • Thanks @MaciejTrojniarz, the index.html file is located on the front end which is hosted at a different Heroku url. Wouldn't I think still need to pass the front end url to the backend so that I can serve index.html? – Daniel S. Sep 04 '19 at 09:11
  • I've added the config.static.json and config.js file as well as the updated server.js file to reference these two added files – Daniel S. Sep 04 '19 at 09:57

2 Answers2

1

I am not into heroku, but I know, that what you need is a reverse proxy (like nginx) which redirects your static traffic to your frontend serving the dist folder and your backend serving you API endpoint.

Alternativeley, you could also serve the dist folder inside your express app using express.static. You will however need to come up with a way to make the frontend build available in your backend. If you publish your frontend to npm, you could just add it as a dependency.

Nappy
  • 3,016
  • 27
  • 39
  • thank you. I'll look for a way to publish the frontend to npm. If this can be done then it sounds like the user should start with the backend heroku url. – Daniel S. Sep 03 '19 at 19:38
0

If your project consist multiple packages you could use f.e. lerna: https://github.com/lerna/lerna to build one deployable unit. And then don't use separate heroku instances for single app.

Maciej Trojniarz
  • 702
  • 4
  • 14
  • 1
    Thanks again @MaciejTrojniarz. I'm trying to keep the heroku url/hostings separate. I guess a better question: Is it better to start at the frontend host and link to the backend or vice versa. It would seem both are possible as a .env can be included in both. – Daniel S. Sep 04 '19 at 23:13