Using Rack::Proxy To Serve Multiple React Apps On The Same Domain

Content posted here with the permission of the author Rahul Ojha, who is currently employed at Josh Software. Original post available here

Background

We had 4 React apps running on the local server. All apps were running on different ports because we can not start multiple React Apps on the same port. We had to build a single sign-on server. Out of 4 react apps one will be responsible for managing authentication and based on roles or department redirecting to other apps. The Auth app will get token from Auth API and will store it to local storage, and other apps will be using this token for authentication while calling APIs.

Problem Faced:

Now while calling API from any React app we need to send the token with it. Since the token is stored in local storage by Auth App which is using a different port so the local storage will not be accessible for other react apps.

You may think “Why didn’t you go with Cookies”, Yes cookies are shareable among HTTP ports but its size is very less which is 4 KB. So if you have to store permissions and anything else which is shareable and exceeding to 4 KB the cookies will not be useful.

Solution:

We will be using a single domain and will access different React apps using namespace in URL.

Example:

localhost:9292/app1 => app1 => localhost:3001
localhost:9292/app2 => app2 => localhost:3002
localhost:9292/app3 => app3 => localhost:3003
localhost:9292/app4 => app4 => localhost:3004

Here we need to use a reverse proxy server to do this. We will use Ruby Rack to create a proxy server. Why not use Nginx here? You can but there is a problem, React does not add namespace in the assets path so assets will not be loaded and you will see a blank white page always, and there is no way in development to add the namespace in asset path without ejecting CRA. If you have specified any value to homepage key in package.json file, then it will be automatically prefixed with assets path while creating a build in production mode.

So if you are Ruby developer then you are going to enjoy rest of the blog. But if you are not a Ruby developer there is no issue because Ruby is nice and easily understandable. You just need to follow the steps.

Step 1:

Install ruby
You can use below link to install RVM and ruby. RVM is Ruby version manager.https://rvm.io/rvm/install

Step 2:

gem install rack

Step 3:

gem install rack-proxy

Step 4:

Create a file ./proxy_server.rb and add below code.

require 'rack-proxy'
class ProxyServer < Rack::Proxy
 def rewrite_env(env)
  request = Rack::Request.new(env)  
  if request.path.match('/app1')
    env["HTTP_HOST"] = "localhost:3001"
    @port = 3001
  elsif request.path.match('/app2')
    env["HTTP_HOST"] = "localhost:3002"
    @port = 3002
  elsif request.path.match('/app3')
    env["HTTP_HOST"] = "localhost:3003"
    @port = 3003
  elsif request.path.match('/app4')
    env["HTTP_HOST"] = "localhost:3004"
    @port = 3004
  # The below line is important to load assets.
  # So it detects app name from the first request to the app
  # which is stored in an instance variable and redirects
  # to that app for assets.
  elsif request.path.match('/static') || request.path.match('/assets')
    env['HTTP_HOST'] = "localhost:#{@port}"
  else
    env["HTTP_HOST"] = "localhost:3001"
    @port = 3001
  end
  env
 end
end

Step 5:

Add a file ./config.ru and add below code

require_relative './proxy_server'
run ProxyServer.new

Step 6:

Run below command on the command line where you have stored above files. This command is responsible for starting your rack server.

rackup

Now your Rack server has started and ready do proxy pass. You just need to go at localhost:9292 and your default app which is app1 as per proxy_server file will be loaded to the browser. Port 9292 is default port defined for Rack server.

Conclusion:

By using Rack server you can run multiple react apps on the same domain in development environment with local storage and cookies shareable among all applications. This solution is not restricted to only React apps, it can be used for any other client-side frameworks too.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.