Heroku/Express: "Not Found" When Attempting To Access A Sub-Route (On Refresh) - node.js

I have a React Web App deployed to Heroku and running on an Express/Node.js server. When someone tries to access any route my server index.js file runs the following code:
const path = require('path')
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
When I load the app from the root route ('/') everything is fine, and as I navigate through the app (using React Router) everything is still fine. The problem happens when I attempt to refresh from a sub-route (e.g., /contact), then I get a "Not Found" error. And when I check the Heroku logs I see this message:
Error: ENOENT: no such file or directory, stat '/client/build/index.html'
I should note I am running the build script on the server, and when I login to Heroku I can see the "build" folder and all contents, including "index.html", are present.
I should also note I have tried other sendFile configurations with no luck, such as:
res.sendFile(path.resolve(__dirname + '/client/build/index.html'))
Does anyone know what the problem might be?

I found out what the problem was, I simply had the path wrong. It should be:
res.sendFile(path.resolve(__dirname + '/app/client/build/index.html'))
Added the '/app' part

Related

express app is not sending index.html file to client

So my express app has a small Node server setup so it can serve up the index.html file when the home route '/' is hit. This is a requirement of using the App Services from Azure, there has to be this server.js file to tell the server how to serve up the client, and i had a previous implementation of this working, however i wanted to change my file structure. previously i had, the client React app in a folder client and the server.js in a folder server along with all of the conrtollers and routes. i've since moved the server API to its own application as there are other apps that depend on it. and i moved the client up one directory into the main directory. Everything was working fine till the other day when all of the sudden when you hit the home route / it will not serve up the index.html file. if you hit any other route it works, if you even hit a button linking back to the homepage, it works, but it wont serve up the app from the / and i cannot for the life of me figure out why, on my development server there are no errors in the console. and im most definitely targeting the correct directory and place for the index. but its like the server isnt reading the route to serve up.
if (process.env.NODE_ENV === 'production') {
console.log('running');
app.use(express.static(path.resolve(path.join(__dirname, 'build'))));
// no matter what route is hit, send the index.html file
app.get('*', (req, res) => {
res.sendFile(path.resolve(path.join(__dirname, 'build', 'index.html')));
});
} else {
app.get('/', (req, res) => {
res.send('API is running...');
});
}
So here im saying if the NODE_ENV is in production make the build folder static, and then whatever route is hit. (Note: i also tried this app.get with other route formats such as /* or / all have the same issues. however in my previous iteration when the client and server where deployed in the same location, /* is what i used.) The .env varialbes are setup correctly, as when the server is ran, itll console log running.. but even if i put a console log inside of the app.get() its like its never hit unless i access the route from something else first.
for example, if i place a console log inside of app.get that states hit whenever the route is hit, hitting / directly does nothing, but if i go to /login itll serve up the correct html on the client and console log hit in the terminal...
If you are having server files inside the client react app, then we are basically accessing file which are not inside our server file. So, we can serve static files using the following code:
const express = require("express");
const app = express(); // create express app
const path = require('path');
app.use(express.static(path.join(__dirname, "..", "build")));
app.use(express.static("build"));
app.listen(5000, () => {
console.log("server started on port 5000");
});
Now in your packages.json of the client react app change the name of start tag under scripts tag to start-client. Then add this following tag to the scripts tag:
"start":"npm run build && (cd server && npm start)",
Basically, this will build the react app and start the server.
It should look like this :
Also in the packages.json of your server add the following tag under script tag
"start":"node server.js"
So when you run the following command npm start it should look like this :

React.js: Heroku: Error: ENOENT: no such file or directory, stat '/app/src/client/build/index.html'

My apologies if this is not the correct way to do this. I have spent 4 days on this problem and eventually figured out a solution I would just like to post this possible solution if anyone else encounters this problem.
I am running a Node.js server with the React client inside of it here is an image of folder structure for reference.
I use a postbuild script in package.json to build my project on heroku
For reasons that are beyond me the following GET catch all statement
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"))
});
Path was returning '/app/src/client/build/index.html' this is the incorrect path as your app is hosted on /app/client/build/index.html I had to edit my GET catch all statement to the following.
app.get("*", (req, res) => {
res.sendFile(path.resolve("client", "build", "index.html"));
});
It now correctly redirects to my index file and the app works correctly. Like I said unfortunately I cant advise on why the __dirname is not pointing to the right folder so if anyone could shed light on that will be great.
Also the robustness of the solution could be a problem as should Heroku change file structures the GET catch all statement could break.
__dirname returns the path to the folder containing the executed file.
So if the code containing __dirname is in the file '/app/src/app.js', __dirname will resolve as '/app/src'.
If your __dirname is in the file '/app/foo/bar/baz.js', it will resolve as '/app/foo/bar'.
Since you want your path to resolve as '/app/client/...' your fix is good.
The solution you used should be robust enough. path.resolve() will set your working directory (the one where you run your app, so '/app' on heroku I assume) as the root of your path. So unless you change your directory names or move files, you should be OK.

Error trying to render the index.html that ng build generates with node and express

I want to deploy an application that I perform with the MEAN stack on Heroku, but I encounter 1 problem.
I have this folder structure, my node server, with a public folder, where is the dist / fronted folder and all the files generated by Angular's ng build --prod, it works when I start the server and browse normally, but if I refresh the page or write a route myself, I get these errors:
Errores
Sorry for my English.
If your are building a MEAN stack, you probably have a server.js or index.js or app.js as an entry point to your application. An SPA by definition manages all the routes within the router configuration. But if you try to refresh or type a route yourself, it is like you were trying to access that folder on the server (ex: www.mywebsite.com/about, here the folder about might not exist on the server, it is just known by your Angular app)
My suggestion is that you try to add this fix to the app.js (or server.js or app.js) file, so all unexisting routes or refresh go back to your index.html:
// Check your port is correctly set:
const port = process.env.PORT || 8080;
// Is saying express to put everything on the dist folder under root directory
// Check the folder to fit your project architecture
app.use(express.static(__dirname + "/dist"));
// RegEx saying "capture all routes typen directly into the browser"
app.get(/.*/, function(req, res) {
// Because it is a SPA, all unknown routes will redirect to index.html
res.sendFile(__dirname + "/dist/index.html");
});
app.listen(port);
This guy shows full deploy on Heroku with Angular: https://www.youtube.com/watch?v=cBfcbb07Tqk
Hope it works for you!

My CRUD app works locally but not on Heroku

I've created a CRUD app and it works locally, but I can't get it to work fine on heroku. It deploys correctly, the website seems to work, but then I can't get the items I need from the database, as it keeps saying connection refused.
I added the .env variables to Heroku, as well as setting the port to process.env.PORT || 5000 and app.listen(port), I'm not sure what's causing the error. I also have a Procfile with web: node server.js, and a "start" script in package.json that points to server.js. It seems that the server doesn't start at all.
Here the repo in case you want to have a look https://github.com/ThomYorke7/inventory, here the app on heroku https://boardgamenerd.herokuapp.com/
The problem lies in the fact that your application has a backend (server) and a frontend (client) which are served differently locally than on Heroku.
I suppose locally your client is running on localhost:3000 (as it is the default with create-react-app you bootstrapped).
While your backend is running on localhost:5000, your client's package.json contains this line to make it work locally:
"proxy": "http://localhost:5000",
If I visit this page of your app: https://boardgamenerd.herokuapp.com/ > boardgames,
then I face these errors on the browser console:
boardgames-list.jsx:18
Error: Network Error
at e.exports (createError.js:16)
at XMLHttpRequest.p.onerror (xhr.js:83)
xhr.js:178
GET http://localhost:5000/boardgames/ net::ERR_CONNECTION_REFUSED
It tells you that your production version still calls backend on localhost:5000.
I.) First I'd try to fix these fetches by changing to relative URLs.
E.g. the above example (boardgames-list.jsx:18)
❌ your current script has hardcoded localhost fetch at the moment:
useEffect(() => {
axios
.get('http://localhost:5000/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
✔️ make it relative to root by removing "http://localhost:5000":
useEffect(() => {
axios
.get('/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
And it will work on Heroku. In case it wouldn't: see my suggestion below.
II.) Second, a suggestion:
Now your https://boardgamenerd.herokuapp.com/boardgames route uses the following backend endpoint to fetch data: https://boardgamenerd.herokuapp.com/boardgames/
The difference is only the last slash ("/") character which can be confusing and cause more issues later!
It is a best practice to add a differentiator path element to your backend endpoints, like /api/. For example: https://boardgamenerd.herokuapp.com/api/boardgames So you can be sure by first sight which GET request related to the backend and which one to the client.
If you'd go with this solution, you will need to add the following to your server.js:
app.use(express.static(path.join(__dirname, 'client', 'build')))
// required to serve SPA on heroku production without routing problems; it will skip only 'api' calls
if (process.env.NODE_ENV === 'production') {
app.get(/^((?!(api)).)*$/, (req, res) => {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'))
})
}
/^((?!(api)).)*$/ regex skips URLs containing "api" in their path, so they won't be served static as the client/build folder's content - api calls won't be served from static and will work fine.

Deploying issue with react,nodejs,express on production

This is my first project using nodejs and react, I have been working a aplication by following this this tutorial.Its working fine localhost
but not working on prodution mode.I have created a build and its generated a directory called "dist". I have moved everthing to live server from "Dist" folder.
But the node route not working , its says 404 error.How to deploy nodejs with react on production?
Can please help me get rid of it?
Thanks
Your backend server will not know how to handle the routes on your client or know the location of your "client" folder, if you have a dist folder you would need to do something similar to this:
if (process.env.NODE_ENV === 'production') {
app.use(express.static('dist'));
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
});
}
Hope this helps.

Resources