react and express: get image from public folder - node.js

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.

Related

How to resolve 'cannot GET' error on react routes when using ExpressJS to serve react build?

I have the following folder structure for a React client and ExpressJS server setup. I'm trying to serve the react build files from the Express server. I also have defined some route handlers for API from the express server
server /* NodeJS server folder
-node_modules
-app.js
-package.json
client /* React client folder
-build
-node_modules
-src
-components
-App.js
-index.js
-package.json
My express (app.js) has the following code
app.use(express.static(path.join(__dirname, '../client/build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});
...
...
app.get('/data', (req, res) => {
res.json(data);
});
Here I'm able to see the home page of my React application but all the routes throws Cannot GET /reactroute. I'm also able to access the /data as defined by app.get handler
Referring some other answers I changed my code in the express server as
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});
...
...
app.get('/data', (req, res) => {
res.json(data);
});
At this time I'm able to access the routes from the react but the /data is not giving the JSON (API) instead it is acting like an another route.
What changes need to be done to get both react routes from the build folder and the route handlers defined in the express.
You have to place get /data before get *.
Like this:
app.get('/data', (req, res) => {
res.json(data);
});
...
...
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});
If you place get * first, it also includes /data. Where * considers all routes which are not mentioned before it.

several react apps with express server

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

how to redirect to react js application using node js, in same server?

I have two different react js Application, and i want to redirect the user to the application depending on the url
example :
app.get('/' , (req,res,next) => {
// redirect to first SPA
});
app.get('/admin' , (req,res,next) => {
// redirect to another SPA
});
If the react app is to be served from the node.js server, simply send each app's index.html:
app.get('/admin', (req, res) => {
res.sendFile('admin_app/index.html');
});
app.get('/', (req, res) => {
res.sendFile('public_app/index.html');
});
If those files are served from a different process on the same machine you will have to proxy the requests:
const httpProxy = require('http-proxy');
const proxy = httpProxy.createServer({});
app.get('/admin', (req, res) => {
proxy.web(req, res, { target: 'http://localhost:3006/admin' });
});
app.get('/', (req, res) => {
proxy.web(req, res, { target: 'http://localhost:3006/' });
});
Redirects would also work, as long as the process at 127.0.0.1:3006 listens on all public addresses. But a redirect includes that the users browser navigates to a different URL. E.g. if your node.js server was running at example.com (i.e. port 80, which the browser omits) and redirects to the other process, the browser would now show example.com:3006. By proxying the request, the URL will stay on example.com.
How about in a route middleware, res.redirect('http://someOtherServer/?
Such as res.redirect('http://localhost:5000').
Maybe this other answer will help Nodejs - Redirect url

React App giving 404 on main js and css

I built a react app using "react-scripts". The application runs perfectly on my local development server but when I deploy to my actual server the applications seems to not find the main JS and CSS files being compiled. I get 404 on both.
Following is the information that might help.
The files on the server are located at
ads/build/static/js and ads/build/static/css || respectively
The 404s I am getting are on the following files:
https://www.example.com/ads/build/static/css/main.41938fe2.css
https://www.example.com/ads/build/static/js/main.74995495.js
Here is how my server is configured:
const express = require('express');
const path = require('path');
const app = express();
const favicon = require('serve-favicon');
//favicon
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.get('/ads', function (req, res) {
app.use(express.static(path.join(__dirname, 'build/static')));
console.log(path.join(__dirname, 'build'));
res.sendFile(path.join(__dirname, '/build/index.html'));
});
app.listen(9000);
In my package.json I have also included the homepage parameter as:
"homepage" : "https://www.example.com/ads
UPDATE
When I run the app on the server itself with the following path:
dedicated-server-host:9000/static/js/main.74995495.js that renders the JS file correctly
Is there some configuration that I am missing, the routing doesn't seem to be working. Please advise.
Use some indentation so you will see error like this:
app.get('/ads', function (req, res) {
app.use(express.static(path.join(__dirname, 'build/static')));
console.log(path.join(__dirname, 'build'));
res.sendFile(path.join(__dirname, '/build/index.html'));
});
You are setting the static route inside of the /ads handler, will add a new express.static route handler on every GET /ads request.
This will set the static route on server startup:
app.use(express.static(path.join(__dirname, 'build/static')));
app.get('/ads', function (req, res) {
console.log(path.join(__dirname, 'build'));
res.sendFile(path.join(__dirname, '/build/index.html'));
});
or:
app.get('/ads', function (req, res) {
console.log(path.join(__dirname, 'build'));
res.sendFile(path.join(__dirname, '/build/index.html'));
});
app.use(express.static(path.join(__dirname, 'build/static')));
But make sure that you get the path right - for example you may need:
app.use('/ads/build/static', express.static(path.join(__dirname, 'build/static')));
if you want the URL in your question to work.
To make it much simpler, you could use just this single handler to make express.static handle both the css/js and index.html at the same time:
app.use('/ads', express.static(path.join(__dirname, 'build')));
and change your index.html to use:
https://www.example.com/ads/static/css/main.41938fe2.css
https://www.example.com/ads/static/js/main.74995495.js
instead of:
https://www.example.com/ads/build/static/css/main.41938fe2.css
https://www.example.com/ads/build/static/js/main.74995495.js
Sometimes getting your paths structure right in the first place can make your route handlers much easier.

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