React / Express - Error: ENOENT: no such file or directory, stat '/app/build/index.html' - node.js

Please see edits below
Please put me out of my misery here. I've spent hours looking through docs and trying different approaches found on this site. I am getting this error when heroku tries to build after pulling code from github:
Error: ENOENT: no such file or directory, stat '/app/build/index.html'
I have my client code in root and my express server code in /server.
Node is being started from the root package.json (e.g. node server/index.js).
If I bash into heroku I can see the /build/index.html file.
app.use(express.static(path.resolve(__dirname, '../build')));
app.get('*', (req, res) => {
res.sendFile(path.resolve('build/index.html'));
});
EDIT
Something else that is curious. If I set the path like so I can browse my images in /build folder on localhost:5000 (same path off of root) but I still get the same error in prod.
app.use(express.static(path.join(__dirname, '/../build')));
EDIT 2:
It turns out that I needed to add a static reference to 'public'. Argh!
I can now see index and anything else in the build directory working.
However, there is no reference to the static/js files that are created during the build and thus the page is empty. I can see them on the server in bash prompt.
app.use(express.static(path.join(__dirname, '..', 'build')));
app.use(express.static('public'));
app.use((req, res, next) => {
res.sendFile(path.join(__dirname, '..', 'build', 'index.html'));
});

I never did find the issue so I scaled it back and had another go of it. Ultimately I don't think you even need to reference the index.html file. At the root level I have server.js. In that file I simply reference the client/build after my route files and this acts as the default for anything not found in the route:
app.use(express.static("client/build"));

I hope this will help other people with this issue. I ran into this error this week and I solved it. In my case it has nothing to do with the client/build/index.html path. And I bet it's the same thing in your case as well.
Please bear with me and go through these points first to understand where the problem comes from:
The problem is that when you create your react app, it somehow runs a git init command automatically. You end up with 2 git folders: one for the Express backend and another for your react client folder.
The result is that when you do commit your changes, those in the client folder are not being pushed to heroku.
To convince yourself that this is the case, run these commands in your terminal at the root of your project:
a) heroku run bash
b) cd into the client folder
c) ls client
if at this stage you don't see the content of your client folder, then that's what caused the problem in the first place.
Now for the remedy:
Go to your terminal and run these commands (credit goes to: Content of create-react-app is not pushed in github)
mv client subfolder_tmp
git submodule deinit client
git rm --cached client
mv subfolder_tmp client
git add client
now make changes in your client folder and then push them to heroku (git add . , git commit -m "", git push heroku master)
Let me know if this helps, I know I solved my problem this way.

Related

Deploying Vue.js + Express + MongoDB App to Heroku

So I've been at this for two days now and can't figure it out. I'm trying to deploy a Vue front end and express back end app to Heroku.
In my root folder I have a "public" folder which contains the front end and "server" folder for the back end.
In the root directory's package.json, the start script is
"start": "npm run pull && start npm run f && start npm run b",
"pull": "git pull",
"f": "cd ./public && npm run serve",
"b": "cd ./server && npm run dev"
This works perfectly fine for me to run locally, and even works with heroku locally as well using heroku local web but when I try to actually push to heroku using git push heroku main it doesn't even get past the git pull claiming:
fatal: not a git repository (or any parent up to mount point /)
I've tried simply getting rid of the git pull, but that just leads down a rabbit hole of other errors I've been trying to figure out for days and I'm hoping I'm just being dumb and missing something obvious. Any help would be greatly appreciated.
I'm going to try and list some things I've tried here:
Setting the heroku remote repository heroku git:remote -a heroku-name-here
Didn't change anything, pretty sure I already have this correct
Removing the git pull
Led to heroku not recognizing "start" sh: 1: start: not found
Removing the 'start' after git pull
Led to vue-cli-service not being found
Literally separating the back and front end into 2 separate repositories and 2 separate heroku things
This seemed to get the back end functioning, but the front end didn't seem to work. Unfortunately I don't remember exactly why but can try again if it would be helpful
All 3 comments from here: How to solve vue-cli-service: not found proplem on heroku?
I kind of fixed it kind of. Here's what I did
So I went back to the approach of kind of keeping them separate. I started by getting the server up and running.
In Heroku, I added the buildpack: https://github.com/timanovsky/subdir-heroku-buildpack.git BEFORE the heroku/nodejs buildpack to allow me to use a subdirectory rather than the whole repo
In Heroku settings, I added the config var PROJECT_PATH with the value server - In this case, server was the sub-directory immediately under my root
In vue.config.js of my front end, I changed the outputted build path to go INTO my server folder, using outputDir: path.resolve(__dirname, "../server/front-end")
In mongodb, under "Security" in "Network Access" which can be found towards the left in the navigation, I added the IP address 0.0.0.0/0 which I believe basically lets anyone access it. This might not be the most secure but it works.
Finally, this basically merged all my routes together which causes chaos because if I had a front end route /test-route and a back-end route /test-route it would like break which made sense. So I simply changed my index.js back end routes to go to /api/ instead of / so kind of like this
const mutators = require("./routes/mutators");
app.use(router.use("/api/", mutators));
Then at the end of my index.js file, I added
app.route("/*").get(function(req, res) {
res.sendFile(path.join(__dirname + "/front-end/index.html"));
});
so that front end routes like /test-route would then actually go to the site.

Express server serving index.html instead of chunk files, in a React app

I'm trying to serve a production build of a React app(Typescript), booted with create-react-app.
I'm following the official guide: https://create-react-app.dev/docs/deployment/
There's nothing unique about my setup. This is the server.js file, located in the root directory(above src):
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(8000);
The production files are created within a folder called build. An example of the paths generated:
<script src="./static/js/main.0ae46692.chunk.js"></script>
When i navigate to localhost:8000/, everything is served fine. The initial request serves the index.html, and the script requests serve the correct files.
But, when i try to navigate(from the browser) to something like localhost:8000/todos, all script requests return index.html.
I do not see anything "special" about my setup, and do not understand what's going on. Am i missing something in the guide? It clearly states that app.get('/*',...) fixes the issue.
Any help will be greatly appreciated.
Copying the comment into an answer here so it can be marked.
This sounds like the issue is the script tags are generated with relative paths, because it works when you make a request to the root /, but not anything else. Could you try setting the "homepage" to "localhost:8000"

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.

Serving static files with Express.js on heroku. Where do files reside in Heroku?

The issue is Heroku can not find the files of my node project. This prompts the 404 Not found error. The question is how does the generic node Heroku file structure look like? If so, where do the files of my project actually reside in the heroku?
The scenario:
With the express library, I put all files needed to serve a frontend template, in a folder called 'public'. The absolute path leading to 'public' is needed and'_dirname' is the variable that contains that.
app.use(express.static(_dirname+'/public');
Now, Heroku always makes a GET request to the root of the application. As a response I send the
index.html file as the starting template. I also provide its absolute path as '{root:_dirname}'.
app.get('/', function(req, res) {
res.sendFile('public/index.html'{root:_dirname});
});
When the server is run, I get a 404 Not found error. Which means Heroku cannot find the files.
Heroku root folder is named app/. So as expected, providing the local '_dirname' is wrong since the application is now running on Heroku server not locally.
2020-09-10T15:28:12.127567+00:00 app[web.1]: Error: ENOENT: no such file or directory, stat '/app/C:/Users/...
But, pointing at the public folder alone(only /public), assuming app/ is root, still prompts the 404 error not found.
2020-09-10T15:31:23.630358+00:00 app[web.1]: Error: ENOENT: no such file or directory, stat '/index.html'
The Question
Which leads to the question, what does heroku file structure look like? where do project files reside in the heroku file structure, so i can provide the correct path on the GET request?
My file structure
After Bergur suggestion:
It prompts a 404 Not found. However, the path is now theoretically correct.
This is what I changed:
app.use(express.static(path.join(__dirname+'/public')));
app.get('/', function(req, res) {
res.sendFile('index.html',{root:__dirname+'/public'});
});
The 404 error comes from the 'app.get('/,function(req,res){});'. Without this handler, my heroku application shows a 'cannot GET' message on the template.
Have you tried to log out the full path just to see if there's something off?
I have several heroku projects with nodejs and react/vue/angular dist folder without problems.
I would change your code a little bit to:
use nodes __dirname
Use path.join instead of inserting the '/' yourself. It might cause some problems.
So final version might look like:
app.use(express.static(path.join(__dirname, 'public')));
This assumes the project structure is
-index.js
-public/index.html
There should be no need to use sendFile, serving the public folder is enough. Just make sure that the public folder is being deployed/pushed to heroku.
If you share with us your folder/project structure we might help you you better.

NodeJS/CloudFoundry - failed: The app upload is invalid: Symlink(s) point outside of root folder

I'm testing CloudFoundry on IBM and are running NodeJS.
When trying to cf push my application I get the following error:
failed: The app upload is invalid: Symlink(s) point outside of root folder
In my appllcation I have the following code:
return res.sendFile(path.join(__dirname +'/tvshows/'+ guide +'.html'));
When not using path.join and simply use:
return res.sendFile(path.join('./tvshows/'+ guide +'.html'));
I get this error instead:
TypeError: path must be absolute or specify root to res.sendFile
What to do?
I've also tried stuff like path.join((process.env.BUILD_DIR || __dirname), and return res.sendFile('index.html', { root: path.join(__dirname, 'tvshows', guide) }); but no luck.
The fail came from my node_modules folder.
Adding .cfignore with node_modules/ fixed the issue.
You didn't mention the version of the cf cli that you're using, but I think that this is expected behavior starting with version 6.34.0.
push now preserves relative symlinks in app files. This makes it easier to work with Node.js apps using npm link, but note that it now errors when it detects a symlink pointing outside the app folder (e.g. a node_modules folder containing external symlinks).
https://github.com/cloudfoundry/cli/releases/tag/v6.34.0
I think you're running into the second part, "how errors when it detects a symlink pointing outside the app folder". It's nothing to do with the code in your app, but rather somewhere in your project folder there is a symlink which references another file that is not under the root of your project.
If you're on a unix-like system you can run find . -type l to find all the symlinks under the current directory. Then you just need to figure out which one is pointing outside of the project root.
Options are to remove that symlink, point it to something under your project root or use .cfignore to ignore that file (which is what you ended up doing).
Hope that helps!

Resources