At Forward Financing, we’ve started using
as a starter kit for all of our new
projects. create-react-app is awesome
because it comes with a one-size-fits-all webpack configuration via the
react-scripts package. All you have to do is
npm run build and you have a
/build folder with minified production asset files.
For our first project using create-react-app, we built an interface to manage our users’ roles and access across our tech ecosystem. The backend server was our standalone single sign-on service, written in Elixir+Phoenix. An API for the new React frontend was built into that backend service.
We had a few requirements for the deployment process of the React app:
- The React app need to live at a subresource of our existing single sign-on
- The React app needed to have a separate deploy process from the backend app
Hosting the React build folder
- Amazon has already optimized those services to do one thing, and one thing only - serve up static files.
- Instead of paying for a whole server, you are only paying fractions of a cent to serve the files.
- If someone wants to launch a DDoS attack against the service providing the assets, they will have to attack Amazon instead of you. S3 offers configuration options to help prevent this.
In order to host the assets cheaply and effectively on S3, we more or less followed this helpful article..
Create a bucket on S3 and make that bucket public
Install aws-cli using your favorite package manager (brew install aws-cli)
Run aws configure with your credentials
Add the following to your
When you run
npm run deploy after having run
npm run build, the contents of
the build folder will be synced to your S3 bucket.
Now your React app should be visible at
Using the hosted assets in our backend app
But we don’t want to mount the React app at
We want to mount it within
our backend service at
In order to mount the React app in our Elixir backend we created a view layout
that matched the
index.html file in the S3 bucket. Next we replaced the
relative asset URLs with the full URL to the file on S3:
Next, we modified the router file in our Elixir app to direct any requests to
paths starting with
/panel/user_roles to the new layout which loads the React
Now, when a user navigates to any path starting with
React layout renders a page with your React app. At that point, React router
takes over, so no more requests will be made to the backend other than those
for the API until the user refreshes the page or navigates out of the React
part of your frontend.
Handling the asset manifest fingerprints
You’ll notice that the compiled asset files produced by
include an 8-digit fingerprint, like
main.123abc45.css. This fingerprint
changes every time you make a change to your app and recompile it. The
goal of this is to indicate to caching systems when the assets change that their
cache needs to be invalidated. So, we need our backend to make a request to
S3 for the asset manifest (which has a constant URL) in order to make sure we
are pointing to the correct version of our assets.
We created a small service module in our Elixir app to contain this logic:
We then used this service module in our controller to get the asset urls and pass them to the view:
Next, we modified the layout to reference the new variables instead of hard coded asset paths:
This setup adds a few milliseconds to the initial React load time as the round trip request is made to S3 for the asset manifest. That request should probably be cached in higher volume production environments.
One feature that would be cool to add to the React build process would be
the ability to deploy compiled assets to different environments. This would
probably involve having the
npm run deploy command take an argument with the
environment name (ie staging, production), and post the assets to the S3 bucket
in a folder with that name. Then the backend staging and production environments
could point to the correct folder, and the assets for each would be separate.
A further spin on that would be to mimic Heroku’s “Review Apps” feature by deploying assets to a folder within the bucket with the same name as the current branch. This would be as simple as:
aws s3 sync build/$(git symbolic-ref --short HEAD) s3://your-bucket --region your-region
Then, reviewers could preview your changes just by going to:
The staging and production environments could point to the assets in the folders for develop and master.