several react apps with express server - node.js

The idea is to make a lot of separate react apps on the one express server, so each of them should appear using the url something like mysite.com/app1, mysite.com/app2 etc
my express code:
const path = require('path');
const app = express();
app.use('/',express.static(path.join(__dirname, './public'))); // just a simple html, not a react
app.use('/app1',express.static(path.join(__dirname, './public/build-1'))); // react
app.use('/app2',express.static(path.join(__dirname, './public/build-2'))); // react
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/app1/*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/build-1', 'index.html'));
});
app.get('/app2/*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/build-2', 'index.html'));
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`)
});
so, actually this works
but there's a problem - when visiting the root url of the site - mysite.com - I get the ordinary index.html file, not a react one, as expected, but using the path - mysite.com/app1 or mysite.com/app2 - the url changes to just mysite.com and the app1 or app2 launches from the root of the site, not staying on the route mysite.com/app1 or mysite.com/app2
and yep, I did configure the react's package.json file for both of the apps
{"homepage": "http://localhost:3000/app1"}
and
{"homepage": "http://localhost:3000/app2"}
the problem has been found: the route I configured in the React App rewrote the url path to the root, so that's why the bug occured

Related

Express.js route almost everything to one request

I want to map all the requests that don't point to static files to my index.html and let javascript do the routing. But if I use the rules below all my requests (including the scripts and styles) hit the * rule.
const express = require("express");
var app = express();
app.get("*", function (request, response) {
response.sendFile(path.join(__dirname, "/index.html"));
});
app.use("/scripts/*", express.static("scripts"));
app.use("/styles/*", express.static("styles"));
app.use("/views/*", express.static("views"));
var port = process.env.PORT || 3000;
app.listen(port, function () {
console.log("Server is running, port: " + port);
});
I've also tried the reverse order.
Just to clarify here are some examples of what the expected behavior is:
/scripts/index.js => /scripts/index.js
/views/app.html => /views/app.html
/anything/here => /index.html
/ => /index.html
/a/lot/of/stuff => /index.html
I want to upload this to heroku and I don't want to use that many free credits. So I want to have all the routing and stuff on the JS to save up on computation on the server.
app.use('/scripts', express.static('scripts'));
app.use('/styles', express.static('styles'));
app.use('/views', express.static('views'));
app.get('/', function (request, response) {
response.sendFile(express.static(path.join(__dirname, 'index.html'));
});
Try to use the code in this manner and I really hope this will work for you
Try this code.
Put all your files inside public folder (including index.html and other folders like script, styles, views)
const express = require('express')
const path = require('path')
const app = express()
const PORT = process.env.PORT || 3000
app.use(express.static(path.join(__dirname, '/public')))
app.get('*', (req, res) => {
res.redirect('/')
})
app.listen(PORT, () => console.log(`Server is running, port: ${PORT}`)
Your code will start working if you shift app.use functions to the top of your code

react and express: get image from public folder

I'm serving 2 different react apps through 1 nodejs backend. Nodejs code (simplified):
const api = require('./api/routes');
app.use('/api', api);
const admin = require('./admin/routes');
app.use('/api/admin', admin);
app.use('/admin', express.static(path.join(__dirname, '/react/admin-client/build/')));
app.get('/admin/*', (req, res) => {
res.sendFile(path.join(__dirname, '/react/admin-client/build/index.html'))
});
app.use(express.static(path.join(__dirname, '/react/client/build/')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '/react/client/build/index.html'))
});
When I navigate to localhost:5000/admin (admin-client app), I see the admin client and everything works just fine. But when I navigate to localhost:5000 (client-app), I see the app, but the assets in the public folder aren't found.
When console.logging process.env.PUBLIC_URL in the admin-client, I get /admin. When I console.log the same thing in the client, I get a blank response.
I'm using an apache vhost to proxy requests from localhost(:80) to localhost:5000.
<VirtualHost *:80>
ServerName localhost
ServerAlias localhost
ProxyPass "/" http://localhost:5000/
</VirtualHost>
When visiting http://localhost/admin/logo.png, I get the logo of the admin-client react app which is in the public-folder.
When visiting http://localhost/logo.png, I don't get the logo of the client react app in which is in the public folder, instead, it's treated like it's a page (a '/' is appended to the url).
I had the same issue. My server is at directory like /appDirectory/config/expressSetup.ts
public app: express.Application;
constructor() {
this.app = express();
this.appClient = express();
this.config();
this.routes();
if (!IS_DEV_MODE) {
this.app.use(express.static(path.join(__dirname, '../../../client/build')));
this.app.use('*', (req, res, next) => {
res.sendFile(path.join(__dirname, '../../../client/build', 'index.html'));
});
}
this.errorHandler();
}
And my client is at /appDirectory/client/build. Make sure you folder which contains icon of files placed inside build folder.

Why can't I view my home page like I can on a local host

I am trying to upload my website on digital ocean using express, node and react. I can view my website on localhost:3000 but when I run nodemon on the publicip:3000 all I see is /root/website/src/index.html displayed on the page. Here is the server.js file
const express = require('express');
const app = express();
//Set port
const PORT = process.env.PORT || 3000;
//Import path
const path = require('path');
//Static files
app.use(express.static('build'));
//Server will use index.html
app.get('/*', (req, res) => {
res.send(path.join(__dirname + '/src/index.html'));
});
app.listen(PORT, () => {
console.log('Listening on port ${PORT}');
});
If you are using res.send() then it will send the path of the file. And path.join should contain the values separated with commas as it takes the values as string array.
Try this
If you want the actual file to send.
res.sendFile(path.join(__dirname ,"src/index.html"));

NodeJS, Express. Cannot upload static content

I've tried to write node server which would run React app created by create-react-app. Actually, something strange happens and I don't have any clue what I'm doing wrong (run app as node server/index.js):
export default (app, dirname) => {
app.use(favicon(path.join(dirname, '..','build', 'favicon.ico')));
app.use(express.static(path.join(dirname, '..','build')));
// initialize routers
bootRotes(app);
if (process.env.NODE_ENV === AVAILABLE_ENVIROMENTS.DEVELOPMENT) {
expressBootDev(app, dirname);
} else {
app.get('/*', (req, res) => {
res.sendFile(path.join(dirname, '..', 'build', 'index.html'));
});
}
}
build folder contains build react app which created the following command npm run build
Strange things are happening when after uploading index page it tries to upload static content. For example http://localhost:5000/static/js/2.30e86b6e.chunk.js. Browser just adds / after each static content url and it turns to http://localhost:5000/static/js/2.30e86b6e.chunk.js/ and of course this url doesn't match to express.static middleware.
Moreover, I've checked via Postman, that url GET http://localhost:5000/static/js/2.30e86b6e.chunk.js withot / at the end provides content which is expected.
I work with PRODUCTION env, it means that expressBootDev doesn't have any impacts.
Has anybody has the same issue? I've spent whole day and don't know hopw to fix it.
When I'm creating a simple code in a root app folder with almost the same logic and run as node server.js and it works as expected:
//server.js
const express = require('express');
const favicon = require('express-favicon');
const path = require('path');
const port = process.env.PORT || 8080;
const app = express();
app.use(favicon(__dirname + '/build/favicon.ico'));
app.use(express.static(__dirname));
app.use(express.static(path.join(__dirname, 'build')));
app.get('/ping', function (req, res) {
return res.send('pong');
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(port);
And I don't see any principal difference
var fs = require('fs');
var express = require('express');
var router = express.Router();
// GET: Sent some basic info for usage
router.get('/', (req, res, next) => {
var fname = __dirname + '/../public/index.html';
var val = fs.readFile( fname, 'utf8', ( err, data) => {
//send can only be called once, write can be called many times,
// in short res.send(msg) == res.write(msg);res.end();
res.writeHeader(200, {"Content-Type": "text/html"});
res.write(data);
res.end();
});
});
module.exports = router;
Here is the example how you can do a static file serving with node.
https://github.com/msatyan/ApiServe1/blob/master/routes/index.js
The full project is
https://github.com/msatyan/ApiServe1
FYI: Node.js with HTTP1 is not an efficient for static file serving by design, I believe HTTP2 support in node has addressed this problem. The reason for inefficiency with HTTP1 is that it has to take the file content read at native layer to JavaScript layer and then send it through HTTP server.

Serving multiple react apps with client-side routing in Express

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 #

Resources