my dir structure
/src
--/public
--/server.ts
--package.json
--package-lock.json
above is my director structure
app.use(express.static(__dirname + "/public/"));
// app.use(express.static("/public/"));
const path = require("path");
app.get("/", (req, res, next) => {
// res.sendFile(path.join(__dirname, + "public", 'index.html'));
res.sendFile(__dirname , "index.html");
//res.send('Testing one two');
});
const port = process.env.PORT || '5005';
app.listen(port, () => console.log("Server running on port 5005"));
when I run the above code, it works well on my local machine but won't work when it is deployed to Heroku,
I tried just passing a string like this and it worked, but when I want to render a static file like the HTML file it wont work on heroku, any help? i think the problem is my directory structure
app.get("/", (req, res, next) => {
res.send('Testing one two');
});
If I recall correctly, express.static middleware is separate from res.sendFile. In other words, even if you set express.static to public, it will not do anything to res.sendFile, as it takes the first parameter as a path.
In my humble opinion, it would be better if you were to use an absolute path, like the following snippet below.
const path = require('path');
/** Code here... **/
app.get("/", (req, res, next) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
Explanations:
path.join is an utility to join path segments into one path. It is cross-platform compatible.
__dirname will get the current directory that the script is running from.
Further reading: Express methods.
You just need to give the address of the index.html file in the path for the code mentioned below and paste this code at the end of the inside of the express file and everything will work perfectly fine and you are good to go.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, './public/index.html'));
});
Related
Which of the following method is preferable to render the static assets. Consider that only helpPage.html is the only file exist in the public directory
Method 1:
app.use(express.static(__dirname + '/public'))
Method2:
app.use((req, res) => {
res.render(__dirname + '/public/helpPage.html');
})
If helpPage.html is the only static file you will deliver, I propose a third option:
app.get('/helpPage.html', (req, res)=>{
res.sendFile( __dirname + '/public/helpPage.html');
});
I don't see the purpose of using app.use here.
Additionally you may want to use path.join:
const path = require('path');
app.get('/helpPage.html', (req, res)=>{
res.sendFile( path.join(__dirname, '/public/helpPage.html') );
});
This ensures that the paths are properly joined regardless of what machine you are running on.
I have different software products for one single service, which needs to be deployed to a single server. The clients are built with react, with a build setup by create-react-app, while the server runs Node.js and Express.
When I serve a single application from the server it is done the following way:
// App.js
// ...
// Entry point for data routes (API)
app.use('/data', indexRoute);
if(process.env.NODE_ENV !== 'development') {
app.use(express.static(path.join(__dirname, 'build-client')));
app.get('/*', function(req, res) {
return res.sendFile(path.resolve( __dirname, 'build-client' , 'index.html'));
});
}
I want to be able to serve multiple apps from the server. How should I do that?
What I tried is to wire in different static paths for the assets and separate the clients with different names, although it did not work. Like this:
// App.js
// ...
// Entry point for data routes (API)
app.use('/data', indexRoute);
if(process.env.NODE_ENV !== 'development') {
app.use(express.static(path.join(__dirname, 'build-client')));
app.use(express.static(path.join(__dirname, 'build-admin')));
app.get('/client/*', function(req, res) {
return res.sendFile(path.resolve( __dirname, 'build-client' , 'index.html'));
});
app.get('/admin/*', function(req, res) {
return res.sendFile(path.resolve( __dirname, 'build-client' , 'index.html'));
});
}
I have also tried to do it this way, but Express throw Error: No default engine was specified and no extension was provided:
if(process.env.NODE_ENV !== 'development') {
// Admin paths
app.use('/admin', express.static(path.join(__dirname, 'build-admin')));
app.get('/admin/*', function(req, res) {
return res.sendFile(path.resolve( __dirname, 'build-admin' , 'index.html'));
});
// Site paths
app.use('/', express.static(path.join(__dirname, 'build-client')));
app.get('/*', function(req, res) {
return res.sendFile(path.resolve( __dirname, 'build-client' , 'index.html'));
});
}
How could I accomplish this or something similar?
After some tinkering I was able to achieve this without using virtual hosts. I used the first idea you gave in the question, except I left the main app at the root (i.e. /).
// when going to `/app2`, serve the files at app2/build/* as static files
app.use('/app2', express.static(path.join(__dirname, 'app2/build')))
// when going to `/`, serve the files at mainApp/build/* as static files
app.use(express.static(path.join(__dirname, 'mainApp/build')))
// These are necessary for routing within react
app.get('app2/*', (req, res) => {
res.sendFile(path.join(__dirname + '/app2/build/index.html'))
})
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/mainApp/build/index.html'));
});
After this, I went into mainApp/package.json and added
"proxy": "http://localhost:4141"
:4141 is the port that the express server is running on. This line will make calls to fetch('/some/route') go back to the server instead of into your react app itself.
Finally, we go to app2/package.json and add
"proxy": "http://localhost:4141/app2",
"homepage": "/app2"
I believe that the key here is the "homepage" key. The way I understand it, when react starts it searches for some static files at its homepage, and without the "homepage" piece I was only able to get either a blank white screen or the mainApp.
I hope this helps someone out there!
EDIT
I have since changed from serving my create-react-apps through my express server to serving them through netlify. Now I don't need to worry about this express setup, or the homepage key in package.json. The express server lives by itself, and the react apps can still both use the same api, and deployment is much easier. Setup with netlify is trivial.
After struggling for a while with this problem I've found a possible solution without compromising the original setup.
We used Express vhost package to setup handling of requests through virtual domains.
When you create your app instance, you should initialize as many apps with express as you want to expose separately (in our case its three separate apps plus the original app instance)
// Create an express instance
const app = express();
const appAdmin = express();
const appClient = express();
const appVendor = express();
After that you need to install vhost and import it. Then with specifying the static folder for each app you can handle serving the static files separately, while the remaining part deals with handling the request for the given subdomains respectively.
appAdmin.use(express.static(path.join(__dirname, 'build-admin')));
appClient.use(express.static(path.join(__dirname, 'build-client')));
appVendor.use(express.static(path.join(__dirname, 'build-vendor')));
appAdmin.use((req, res, next) => {
return res.sendFile(path.resolve( __dirname, 'build-admin' , 'index.html'));
});
appClient.use((req, res, next) => {
return res.sendFile(path.resolve( __dirname, 'build-client' , 'index.html'));
});
appVendor.use((req, res, next) => {
return res.sendFile(path.resolve( __dirname, 'build-vendor' , 'index.html'));
});
app.use(vhost('domain.com', appClient));
app.use(vhost('www.domain.com', appClient));
app.use(vhost('a.domain.com', appAdmin));
app.use(vhost('b.domain.com', appVendor));
Don't forget to add the desired subdomains in your domain's DNS registry. Example:
...records
CNAME vendor #
CNAME admin #
I wrote the following code:
var express = require('express');
var app = express();
app.use('/', express.static(__dirname ));
app.get('/', function (req, res) {
res.sendFile('./dist/index.html');
});
app.listen(3000, function() {
console.log("Listening on port 3000");
});
which doesn't work. When open the browser and go to "localhost:3000" I get the error:
path must be absolute or specify root to res.sendFile
Of course the once I fix the line that starts with "app.use..." to:
app.use('/', express.static(__dirname + "./dist"));
then everything works right.
Can you please explain why? What's wrong with giving "express.static" a path of a parent folder of the direct folder of the file sent?
Try changing the order. Instead of:
app.use('/', express.static(__dirname ));
app.get('/', function (req, res) {
res.sendFile('./dist/index.html');
});
Try:
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, './dist/index.html'));
});
app.use('/', express.static(__dirname));
// OR:
app.use('/', express.static(path.join(__dirname, 'dist')));
Plus use path.join() to join paths. You need to require path first:
var path = require('path');
See this answer for more info on serving static files and why path.join is important:
How to serve an image using nodejs
Now, your problem was not about express.static but about res.sendFile. When you changed express.static path to a different one, then the file you previously wanted to send with res.sendFile was probably found by express.static and the handler with res.sendFile was not run at all. Before the change, the express.static wasn't finding the index.html file and was passing the request to the next handler - which had a bad res.sendFile invocation. That's why it appeared to solve the problem with the error, because the code causing the error was no longer called.
I have a node server, that serves static files in a PUBLIC folder like this:
var app = express();
app.listen(port);
app.use(compression());
app.use(express.static(__dirname + '/PUBLIC'));
There is a json file, let's say important.json that is located in /PUBLIC folder. This is being served as a static file
Now, I want to intercept request for this /PUBLIC/important.json, so that I can programatically return a random json structure instead.
None of the followings works:
app.get('/PUBLIC/important.json', function(req, res) {
console.log("caught1!")
});
app.get(__dirname + '/PUBLIC/important.json', function(req, res) {
console.log("caught2!")
});
app.get('important.json', function(req, res) {
console.log("caught3!")
});
How can I intercept request for that partically static file?
As the express.static middleware does not call the next middleware using next(), the definition order is important. You have to define your own middleware before using express.static.
app.get('/PUBLIC/important.json', (req, res, next) => {
console.log('caught');
next();
});
app.use(express.static(__dirname + '/PUBLIC'));
Could you tell us a bit more about your stack ?
Are you using nginx / apache to proxy_pass the traffic to your nodejs server ?
Are you just running your app with "node app.js"
Let's try to add this simple route in your application :
app.get('/', function (req, res) {
res.send('Hello World!');
});
And try to access it by removing URI parameters ? Does the "Hello world" show up ?
I just want to be sure the traffic is actually treated by your node app.
Your route definition is supposed to work for your actual request.
NOTE : This is NOT a duplicate question, I've already tried other answers to similar questions.
I'm trying to render html files (Angular) but I'm having an issue.
This works.
app.get('/randomlink', function(req, res) {
res.sendFile( __dirname + "/views/" + "test2.html" );
});
But I don't want to copy and paste dirname thingy over and over, so I tried this in order to not to be repetitive with urls:
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'views')));
app.get('/randomlink', function(req, res) {
res.sendFile('test2.html'); // test2.html exists in the views folder
});
Here's the error.
My express version is 4.13
path must be absolute or specify root to res.sendFile
If you look into the express code for sendFile, then it checks for this condition:
if (!opts.root && !isAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
So You must need to pass Absolute path or relative path with providing root key.
res.sendFile('test2.html', { root: '/home/xyz/code/'});
And if you want to use relative path and then you can make use path.resolve to make it absolute path.
var path = require('path');
res.sendFile(path.resolve('test2.html'));
You can't go against official documentation of res.sendFile()
Unless the root option is set in the options object, path must be an absolute path to the file.
But I understand that you don't want to copy smth like __dirname every time, and so for your purpose I think you can define your own middleware:
function sendViewMiddleware(req, res, next) {
res.sendView = function(view) {
return res.sendFile(__dirname + "/views/" + view);
}
next();
}
After that you can easily use this middleware like this
app.use(sendViewMiddleware);
app.get('/randomlink', function(req, res) {
res.sendView('test2.html');
});
Easiest way is to specify the root:
res.sendFile('index.html', { root: __dirname });
I was facing the same problem then i solved my issue as follows.
const path = require("path")
app.get('/', (req, res)=>{
res.sendFile(path.resolve("index.html"))
}
Good Luck
the issue happened to me when I was providing subpaths. I set static path and had this route:
const public = path.join(__dirname, "..", "live", "public");
app.get("*", (_, res) => {
res.sendFile("index.html");
});
Visiting "/" path worked but "/anyOtherPath" did not work. So I change the route:
app.get("*", (_, res) => {
res.sendFile(public + "/index.html");
});