I have a react app, using Postman for testing, to get HTTP requests runing on a Heroku server with client and server in a single root folder.
After deployment, I have run into a problem with the react-router on the Heroku server, where I get a CANNOT Get
error after refreshing a page or manually entering the endpoint.
To solve this error I added a wild card endpoint to redirect the route back to index.html in server.js (code below).
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});
Once the above error was fixed I have run into another problem. I am now unable to get the JSON data via the GET request
, and instead in Postman html.index
is returned. To solve this problem I removed the code above, however, this solution revolves back to the first error and I am stuck in a circular loop, of fixing the first error and dealing with the second error.
I believe I can write an if statement that checks the route to redirect to index.html only when needed and this is what I have tried below, but it doesn't work and I am not sure how to write out the if statement to fix these two revolving errors.
app.get("*", (req, res) => {
let url = path.join(__dirname, '../client/build', 'index.html');
if (!url.startsWith('/app/')) // <-- not sure what to replace here to make code work
url = url.substring(1);
res.sendFile(url);
});
How can I fix these two errors with an if statement or another solution dealing with react-router and the Heroku server?
my code server.js
const mongoose = require("mongoose");
const path = require("path");
const express = require("express");
const app = express();
const cors = require("cors");
const logger = require("morgan");
const PORT = process.env.PORT || 3001;
require("./models/CalanderData");
const router = require("./routes/routes");
app.get("/api", (req, res) => {
res.json({ message: "Hello from server!" });
});
app.use(express.static(path.join(__dirname, '../client/build')));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(logger("dev"));
app.get("*", (req, res) => {
let url = path.join(__dirname, '../client/build', 'index.html');
if (!url.startsWith('/app/')) // <-- not sure what to replace here to make code work
url = url.substring(1);
res.sendFile(url);
});
//MonogoDB code to access DB
app.use("/", router);
app.listen(PORT, () => {
console.log(`Express running PORT ${PORT}`);
});
routes.js
const express = require("express");
const router = express.Router();
const calDataController = require("../controllers/CalDataController");
router.get('/get', calDataController.getData);
// I have other routes but /get is the one I am testing
module.exports = router;
CodePudding user response:
There's a handy piece of information in the Create React App documentation around deployment and client-side routing.
Your Express app should have the following
app.use(logger("dev"));
app.use(cors()); // always register CORS before other request handling middleware
app.use(express.json()); // you only need this once
app.use(express.urlencoded()); // do you even need this?
// Register API routes
app.get("/api", (req, res) => {
res.json({ message: "Hello from server!" });
});
app.use("/", router);
// Register client routes / middleware
const CLIENT_BUILD_DIR = path.join(__dirname, "../client/build");
app.use(express.static(CLIENT_BUILD_DIR));
app.get("/*", (req, res) => {
res.sendFile(path.join(CLIENT_BUILD_DIR, "index.html"));
});
The main thing to note is that that catch-all route is defined as /*
and is registered last. This is so it does not conflict with other routes.
CodePudding user response:
I am posting my solution which is Phils above however, I am using controllers because I don't want to have a bunch of routes on my app.js on the server side, so with that, I need to have the Database code BEFORE the app.use("/", router);
or the app breaks.
As far as the order Phil is correct, and that coupled with keeping the MongoDB code before the route request worked :)
const mongoose = require("mongoose");
const path = require("path");
const express = require("express");
const app = express();
const cors = require("cors");
const logger = require("morgan");
const PORT = process.env.PORT || 3001;
require("./models/CalanderData");
const router = require("./routes/routes");
app.use(logger("dev"));
app.use(cors()); // always register CORS before other request handling middleware
app.use(express.json()); // you only need this once
//MONGO DB CODE
// Register API routes
app.get("/api", (req, res) => {
res.json({ message: "Hello from server!" });
});
// Register client routes and middleware
const CLIENT_BUILD_DIR = path.join(__dirname, '../client/build')
app.use(express.static(CLIENT_BUILD_DIR));
//notice the order here
app.use("/", router);
app.get("/*", (req, res) => {
res.sendFile(path.join(CLIENT_BUILD_DIR, "index.html"));
});
app.listen(PORT, () => {
console.log(`Express running PORT ${PORT}`);
});