Skip to content

Instantly share code, notes, and snippets.

@slamb2k
Last active May 5, 2024 17:39
Show Gist options
  • Save slamb2k/30b026c2b9f0b652b686fd3863970772 to your computer and use it in GitHub Desktop.
Save slamb2k/30b026c2b9f0b652b686fd3863970772 to your computer and use it in GitHub Desktop.

Web Application Delivery Best Practices

This is a summary of popular methods for incorporating the "Build once, deploy everywhere" principal of DevOps when delivering any type of server based application. The main idea is to use the same bundle for all environments, from testing to production. This approach enables easy deployment and testability and is considered a fundamental principle of continuous delivery. Supporting resources are included below and will be referenced where possible.


Single Page Applications

In static web applications, the configuration is baked in at build time, meaning you most likely need to create individual build artifacts for dev, test, uat, live etc. This can turn into a significant time sink if the build process is even remotely lengthy.

In a ReactJS (create-react-app) application, In we commonly utilise different configuration parameters using the REACT_APP.env properties—but those must be set at compile time. Create-react-app expects you to rebuild the application for each target environment, which violates the principle. And it seems that it is the most common approach nowadays. But what if you want, or need, to follow the build once, deploy many principle?

The goal is to decouple environment-specific configuration from the build process, allowing the same build artifact to be deployed to multiple environments with different configurations.

There are a number of different strategies to achieve this goal but some of the most popular are listed below with their relative effort/complexity, pros and cons.

1. Dynamic Configuration via Public Folder:

  • Approach: Store environment-specific configuration files like config.js in the public folder of the React application, which is not processed by Webpack.
  • Effort: Moderate setup effort to organize configuration files and modify the build pipeline.
  • Pros: Simplifies the CI/CD pipeline by using the same build for all environments.
  • Cons: Configuration files are publicly accessible, which might be a security concern.
  • Source: Discussed in the Cevo blog.

2. Environment Variable Injection at Runtime:

3. External Configuration File:

  • Approach: Use an external config.json that is fetched at application startup to load configuration dynamically.
  • Effort: Low to moderate effort, involving creating and managing the config file outside the build process.
  • Pros: Easy to update without needing to rebuild or redeploy the application.
  • Cons: Potential delay in application startup due to the fetch operation.
  • Source: Explained in detail on mikesir87's blog.

4. Using CI/CD Pipeline for Environment Switching:

  • Approach: Automate the switching of environment configurations during the deployment phase without rebuilding.
  • Effort: High setup effort to script and test the CI/CD pipeline steps.
  • Pros: Builds are consistent across all environments; reduces build time.
  • Cons: Complexity in pipeline setup and maintenance.
  • Source: Commonly referenced in discussions about DevOps practices for SPAs.

5. Server-Side Rendering for Dynamic Injects:

  • Approach: Use server-side rendering to dynamically inject environment variables into the application at runtime.
  • Effort: High due to the need for server-side capabilities and additional coding.
  • Pros: Offers greater control over environment-specific configurations.
  • Cons: Increases complexity and resource usage of the application.
  • Source: Discussed in the context of React and Redux documentation.

6. Webpack Environment Plugin:

  • Approach: Utilize Webpack's EnvironmentPlugin to manage environment variables during the build process.
  • Effort: Moderate effort to configure Webpack appropriately.
  • Pros: Integrates smoothly into existing Webpack builds, easy to use once set up.
  • Cons: Still requires a build per environment if variables are used in the code directly.
  • Source: Detailed in Webpack's official documentation.

7. Containerization:

  • Approach: Package the application in a container that can be deployed across any environment.
  • Effort: High initial effort to set up Docker or other container technologies.
  • Pros: Highly portable and consistent across all deployment environments.
  • Cons: Requires container management and orchestration knowledge.
  • Source: General knowledge and best practices in cloud-native development.

8. Injecting Global Variables:

  • Approach: Consider all static assets as immutable and utilise the public scope to host an index.html that is unique to each environment. By including versioned references to the web application static assets and setting the environment variables in the index.html, it effectively becomes a deployment manifest.
  • Effort: Low to medium effort to update index.html in your deployment process for each environment and update code to use window.env rather than process.env.
  • Pros: Highly portable and consistent across all deployment environments.
  • Cons: Configuration files are publicly accessible, which might be a security concern.
  • Source: Injecting Data from the Server into the Page

Conclusion

While the strategies list above each have there own benefits, I feel like the last one is the easiest to understand and requires minimal effort to modify an existing React JS based application to take advantage of it. Simply add the "environment variables" to the index.html in your public folder as it won't be bundled:

image

Use some kind of placeholder like in the example above so that when you deploy the build, it will be easy to quickly update this file. Next, build the app using npm build or yarn build, etc.

Next, simply update any usage of the process.env to retrieve environment variable values to instead use window.env:

image

That's it! With these changes in place, you can now deploy the production version but all of the environment variables will be defined in the index.html which is not bundled or minified. Use any search and replace tool to find the placeholders you put in the file and substitute them for your true configuration values. After the server hosting the static web app is restarted, the values will be in effect.


References

How to "Build Once and Deploy Many" for React App in CI/CD - Cevo https://cevo.com.au/post/how-to-build-once-and-deploy-many-for-react-app-in-ci-cd/

A Build Once, Deploy Many Implementation Guide for React Apps with GitHub Actions and React-Inject-Env | by Liam Patty | Medium https://medium.com/@liamhp/build-once-deploy-many-for-react-apps-with-github-actions-and-react-inject-env-f56aa78ffa44

Build Once and Deploy Everywhere for SPAs – mikesir87's blog https://blog.mikesir87.io/2021/07/build-once-deploy-everywhere-for-spas/

Build once, deploy many in React | Profinit blog https://profinit.eu/en/blog/build-once-deploy-many-in-react-dynamic-configuration-properties/

React Application: Build Once, Deploy Anywhere Solution - DEV Community https://dev.to/eamonnprwalsh/react-application-build-once-deploy-anywhere-solution-507m

Environment Variables in JavaScript: process.env https://dmitripavlutin.com/environment-variables-javascript/

Managing Front-end JavaScript Environment Variables https://robertcooper.me/post/front-end-javascript-environment-variables

Title and Meta Tags | Create React App https://create-react-app.dev/docs/title-and-meta-tags/#generating-dynamic-meta-tags-on-the-server

reactjs - How to inject pod environment variables values into React app on runtime? - Stack Overflow https://stackoverflow.com/questions/70085518/how-to-inject-pod-environment-variables-values-into-react-app-on-runtime

EnvironmentPlugin | webpack https://webpack.js.org/plugins/environment-plugin/

Server Rendering | Redux https://redux.js.org/usage/server-rendering#inject-initial-component-html-and-state

The Most Common XSS Vulnerability in React.js Applications | by Emelia Smith | Node Security | Medium https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0

Injecting Data from the Server into the Page | Create React App https://create-react-app.dev/docs/title-and-meta-tags/#injecting-data-from-the-server-into-the-page


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment