deploying create-react-app to heroku with express backend returns invalid host header in browser
Asked Answered
S

1

6

The title says it all. I've built a minimal working example here: https://github.com/sehailey/proxytest

I've tried so may things I've lost count (though they're stored in the commits). I promise once I get an answer to follow up on the dozens of answered threads asking this question.

Sheepdip answered 17/10, 2018 at 0:44 Comment(0)
A
7

Looks like they changed how the create-react-app utilizes a proxy. Remove the proxy from the package.json. Then...

Add this package:

npm i -S http-proxy-middleware

Then create a setupProxy.js in src:

src/setupProxy.js

const proxy = require("http-proxy-middleware");

module.exports = app => {
  app.use(proxy("/api/*", { target: "http://localhost:5000/" }));
};

Now from inside the React component, you can do this:

src/App.js

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

export default class App extends Component {
  state = {
    message: "",
    error: "",
    eee: "",
    text: ""
  };

  componentDidMount = () => this.fetchAPIMessage();

  fetchAPIMessage = async () => {
    try {
      const res = await fetch(`/api/message`);
      const { message } = await res.json();
      this.setState({ message });
    } catch (err) {
      console.error(err);
    }
  };

  render = () => (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>WELCOME CREATE REACT APP!</p>
        <div className="App-link">{this.state.message}</div>
      </header>
    </div>
  );
}

index.js (I added npm i -D morgan which is a handy logging framework -- when a request hits the API, it displays it in the console).

const path = require("path");
const express = require("express");
const app = express();
const morgan = require("morgan");

app.use(morgan("tiny")); // logging framework

// Serve our api message
app.get("/api/message", async (req, res, next) => {
  try {
    res.status(201).json({ message: "HELLOOOOO FROM EXPRESS" });
  } catch (err) {
    next(err);
  }
});

if (process.env.NODE_ENV === "production") {
  // Express will serve up production assets
  app.use(express.static("build"));

  // Express will serve up the front-end index.html file if it doesn't recognize the route
  app.get("*", (req, res) =>
    res.sendFile(path.resolve("build", "index.html"))
  );
}

// Choose the port and start the server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Mixing it up on port ${PORT}`));

package.json (use node to serve production assets -- see "start" script)

{
  "name": "proxytest",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://proxytest2.herokuapp.com/",
  "dependencies": {
    "concurrently": "^4.0.1",
    "express": "^4.16.4",
    "http-proxy-middleware": "^0.19.0",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-scripts": "2.0.5",
    "serve": "^10.0.2"
  },
  "scripts": {
    "start": "NODE_ENV=production node index.js",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "client": "react-scripts start",
    "server": "nodemon server",
    "eject": "react-scripts eject",
    "dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\"",
    "heroku-postbuild": "npm run build"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

What you'll see in the console when running in production:

m6d@m6d-pc:~/Desktop/proxytest-master$ npm start

> [email protected] start /home/m6d/Desktop/proxytest-master
> NODE_ENV=production node index.js

Mixing it up on port 5000
GET / 200 2057 - 6.339 ms
GET /static/css/main.068b9d02.chunk.css 304 - - 1.790 ms
GET /static/js/1.9a879072.chunk.js 304 - - 0.576 ms
GET /static/js/main.e3ba6603.chunk.js 304 - - 0.605 ms
GET /api/message 201 36 - 4.299 ms
GET /static/media/logo.5d5d9eef.svg 304 - - 0.358 ms

Other notes:

  • Make sure to separate your client src from the API (for example, put everything from the front-end React application into a client folder with its own dependencies).
  • Remove any and all React dependencies from your API's package.json
Autobus answered 17/10, 2018 at 1:30 Comment(13)
fantastic answer, nice work and thanks! I'll be sharing with the dozens of others and hope it can save someone else some frustration int the future.Sheepdip
So, I am using localhost:3000 to host my CRA client and localhost:3001 to host my Express Server. Adding src/setupProxy.js and pointing to app.use(proxy("/api/*", { target: "http://localhost:3001/" })); in my client/src folder is not working at the moment. Any ideas?Granada
Since I don't have your code, I can't say for sure where your problem lies.Autobus
I am actually experiencing the same problem on Azure: #52970871Granada
Does this answer work at all? How does setupProxy.js get used? I was referred to this answer, but it doesn't help in my case, #53056712Brassard
@MattCarlotta when you do proxy("/api/*"...) instead api I can just put proxy(" /* ", ...)? and the target is front-end url or back-end?Raspings
The proxy should point to the backend. In the example above, the backend is served on port 5000 (http://localhost:5000/api). In production, when everything is served on a single port, using just / can interfere with client side routes if they share the same route name -- which is why I append all backend routes with /api.Autobus
the setupProxy has to be in the server right? can we just put the proxy in the first lines of the server? your example is not working for meOneiromancy
Negative. The create-react-app has been (or was at the time) automatically set up to look for a setupProxy.js within the root of the src folder (similar to setupTests.js).Autobus
hi, in your server you do a "if (process.env.NODE_ENV === "production") but when I console log this, I always have undefined, my NODE_ENV env variable is always at undefined, but I did like you, do you know what can create his issue ?Oneiromancy
You have to set it in your package.json scripts (see start example above).Autobus
@MattCarlotta please when you'll have 5mins, can you check my topic ? I did like you did but 2 days I'm struggling with this.. #59554851Oneiromancy
Will take a look at it over the weekend.Autobus

© 2022 - 2024 — McMap. All rights reserved.