Simple NodeJs Reverse Proxy for a React App and its API Backend
How to integrate a React app with an API back-end without messing everything up. A simple implementation of a reverse proxy with NodeJs to serve a React App and offer an API gateway.
The easiest configuration for a simple React web app is to serve front-end resources from the same host and port as the back-end implementation.
This solution has several advantages:
- almost removes the need of environment configuration on the front-end
- no struggling with custom configurations
- no need of repeated builds per environment
- removes the need of CORS
And disadvantages:
- using a same port implies at least two machines
- or an awful copy of front-end artifacts in the back-end environment
- or worse, a copy of front-end artifacts in the back-end ones
In such configuration, webpack-development-server
offers a great solution to serve both front-end resources and a proxied access to an API back-end. Moreover, it comes pre-configured with Facebook's create-react-app
generator. But what to do if we want to keep the same approach in production without adding more technology entropy?
So these are my objectives:
- Complete independence of back-end and front-end artifacts and runtimes
- Build Once Run Everywhere approach to ease Continuous Delivery and Integration
- Easiest solution without introducing other languages, components or complex configurations
Next, I will describe how to implement a simple reverse proxy with NodeJs to serve the React app and offer an Api gateway. Basically, it consists in a single NodeJs script and a Docker image, configurable via environment variables.
The server.js script
The server.js
file is the single node script that provides all the needed functions. The project layout I am considering is the one of Facebook's create-react-app
builder. So the script expects the static resources to be in the "build" folder.
var express = require('express');
var compression = require('compression');
var httpProxy = require('http-proxy');
var API_HOST = process.env.API_HOST || 'localhost:8081'
var PORT = process.env.SERVER_PORT;
// Initialize
var app = express();
var apiProxy = httpProxy.createProxyServer();
// Serve static resources from 'build' folder
app.use(express.static('build'));
// Enable gzip response compression
app.use(compression());
// Proxy all the api requests
app.all('/api/*', function (req, res) {
apiProxy.web(req, res, { target: 'http://' + API_HOST })
});
// Otherwise serve index.html
app.get('*', function (req, res) {
res.sendfile("/build/index.html");
});
app.listen(PORT);
console.log('Running on port' + PORT + ' with API_HOST:'+API_HOST);
The Dockerfile
This is a simple Dockerfile that builds the React app and starts a NodeJs instance.
FROM node:9.2-alpine
ADD ./ /
RUN npm install && npm run build
ENV API_HOST UNDEFINED_HOST
ENV SERVER_PORT 8080
ENV NODE_ENV production
EXPOSE 8080
ENTRYPOINT [ "sh", "-c", "node server.js" ]
The build steps are the following:
- move source code to the docker daemon
- install npm dependencies
- build React App
Configuration is provided by mean of environment variables that can be overwritten at runtime (e.g. with docker run --env <key>=<value>
). The entry point simply executes the server.js script.
Final considerations
Although it may be not the best choice for large scale deployments, this very simple solution fits a lot of common use cases and provides great flexibility:
- small amount of configuration
- friendly environment for front-end devs
- can be easily deployed in a container based CI pipeline
- can be extended and customized as needed with very little efforts (e.g error messages)
- integration for session-based authentication systems can be added with other dependencies, allowing the underlying REST API to be stateless.
Thanks for reading 🤓