sznrbrt sznrbrt - 10 months ago 108
Node.js Question

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?

Answer Source

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 as C records in your domain's DNS registry. Example:

...records
CNAME   vendor  @
CNAME   admin   @
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download