Hosting multiple apps on the same server — Implement a reverse proxy with Node
Whenever I choose a new cloud-hosting plan in order to have a remote server for my developments, I’m often to run multiple apps on it.
There are many reasons to this, for instance:
- It allows me to keep my computer clean from the garbage that comes with downloading and installing tons of IDEs, libraries, packages, etc.
- It allows me to have a server running 24/7 so that my customers can follow my progression whenever they want.
- It removes the hassle of configuring my box to allow public visitors on my projects.
- Basically a physical server is a computer just like any other, so why would I host only 1 app per server ?
Now comes the implementation part, and if you’ve tried yourself at this exercise you know that it often ends like this :
What has to be noticed here is that once you’re running a project, it listens on a specific port and you have to keep a list of which ports are used if you don’t want to stumble across a fatal error :
EADDRINUSE: This port is already in use
The other pain point is that when you’re interacting with your program, your requests will look something like this :
http(s)://domain.ext:port/route
With port
being the port your project listens on.
The goal of this article is to teach you how to create a reverse proxy so that in the end you’ll be able to use meaningful urls for your projects.
Just like that:
http(s)://domain.ext/nameOfYourProject/route
Instead of having to remember which port your app is listening on.
First, what is a reverse proxy ?
A picture is worth thousand words.
In brief, a reverse proxy will match incoming requests with their component.
It’s just a request organizer. Its role is to redirect requests based on criteria (URL parts, request body, basically anything can be a criterion) and to give the response back from the component the request has been redirected to.
In this example, the reverse proxy running on port 443 will proxy REST requests to the Python REST API listening on port 5000 and it will proxy website requests to the Apache server listening on port 80.
For this particular case (a real use-case I dealt with), the criterion was the URL part after the domain name, like this :
https://website.com/rest
-> Python REST API
https://website.com/web
-> Apache web server
How to implement one ?
Before we start, make sure that you have the following :
- Node : https://nodejs.org/en/
- Express :
npm install express
- HTTP Proxy Middleware :
npm install http-proxy-middleware
Express will allow you to run a server, and HTTP Proxy Middleware will proxy requests to the endpoints you configured.
Finally, we’ll use a JSON config file which will contain a mapping like this :
It means that once /project1
is requested, the request is proxied to http://localhost:1001
.
Assuming you created a JSON file and a JS file to run a server, this is what you should end with:
/project
app.js
config.json
And this is the code you have to insert into app.js
:
The explanation is:
Line 6
: Turning the JSON file into a JS object viarequire
Line 10
: Iterating over all the routes, one by oneLine 11
: Usingapp.use(route,handler)
means that no matter the HTTP method used in the requests, the server will usehandler
whenroute
is requested.Line 12
: Create an anonymous handler which will proxy requests (documentation here)Line 13
: The target is where the request will be sent toLine 14
:pathRewrite
can be a function. We defined ours so that it strips the sugar part. Let me explain.
To access project1
, you will open your browser and type: http://domain.ext/project1/route/parameters/blabla
When called, pathRewrite
will fill the path
parameter with what comes juste after the domain part.
In our case, path
will be equal to : /project1/route/parameters/blabla
But in fact, your app only needs what comes after /project1
.
That’s how you get rid of it :
path.split('/')
=['','project1','route','parameters','blabla']
.slice(2)
=['route','parameters','blabla']
.join('/')
=route/parameters/blabla
(the leading/
beforeroute
isn’t necessary)
Line 21
: Run the server and you’re good to go 😃
I recommend running your reverse proxy daemonized with a crash-proof launcher like pm2
or any other utility so that if it breaks it will restart automatically without compromising the projects it redirects to.
For instance :
pm2 start app.js
You should now be able to use meaningful URLs to access your different projects on the same server, without having to specify the port manually for each request.
Note that this reverse proxy would also work using https
(that’s what I’m personally up to).
Author’s note
This article brings a useful trick I often use in my daily developer life.
You could be thinking “well, I don’t have to type the port anymore but I still have to type a keyword”. Which one is more meaningful to you ?
Like always, you’re free to make your choices.
Thanks for reading
My last article is available here : Node + Express + LetsEncrypt : Generate a free SSL certificate and run an HTTPS server in 5 minutes or less
I appreciate comments, suggestions and debates.
Feel free to reach out at david.mellul@outlook.fr 😃 ☕️