Firebase + Next.js serverless, on GCP - How to manage staging, production + local - reactjs

I'm using React with next.js and Google Cloud functions to serve the app. I also use firebase. I'm looking for the best way to automatically configure the staging and production configuration for the 3 environments.
Production: Uses production credentials
Staging: Uses staging credentials
Local: Also uses staging credentials
I have two Firebase projects and currently switch between the configuration using the firebase.js file in my app. I swap out the config object, then deploy. I want to be able to run the app locally, and both on staging and production without changing anything on deploy as it leads to errors.
The issue for me is setting different environment variables for the two cloud projects...I can read cloud environment variables I set up there, but only in the cloud functions environment, not in the client-facing app, which is where I am currently swapping the configuration.
I can see this being solved by:
Google Cloud Platform environment variables (but I have tried and failed to read them from the client). Maybe I change the next.js config to read something up in the cloud when running, instead of doing the config change at deploy?
Local nextjs environment configuration but this doesn't know anything about the two different servers (only dev vs prod which don't match up exactly with local and cloud)
React dotenv configuration (same as the point above)
webpack / npm configuration that swaps the config when compiling
Swapping env based on firebase use [environment] on the command line at deploy time
Points #1 and #5 seem the most likely candidates for automatic and seamless deployment but I can't figure out how to read the GCP config variables in React and for #5 I don't know how to run a custom script that could swap variables based on the firebase project currently being used on the command line.
Most info I have seen on this doesn't tackle this issue exactly - all the env variables are either only in the cloud functions or distinguish local vs cloud or dev vs prod, but cannot distinguish between two clouds and a local that uses the same config as one of the clouds.
Someone must have had experience with this?

I've finally tracked down an answer to this.
The best way I've found to handle this in React/nextjs is to use a combination of my ideas #4 and #5 from my original question.
As seen here, firebase has a firebase apps:sdkconfig command on the cli that:
Prints the Google services configuration of a Firebase App.
So knowing that, you can add a script to npm that creates a firebase config file each time the site gets built. Since it knows which firebase project you are currently using on the command line, it knows the version of the config to output when you're deploying.
Then, in your package.json, you can set it to run.
"scripts": {
...
"build": "npm run getconfig && next build",
"getconfig": "firebase apps:sdkconfig web --json > ./firebase-config.json",
...
},
Then, in your firebase.js or wherever you're configuring firebase in your app:
// Get the Firebase config from the auto generated file.
const firebaseConfig = require('./firebase-config.json').result.sdkConfig;
// Instantiate a Firebase app.
const firebaseApp = firebase.initializeApp(firebaseConfig);
This works both locally and in the cloud with no manual changes needed other than what you'd already be doing to switch the environment you're using (i.e. firebase use)
You might need to tweak which npm script it runs on or other small things to suit your needs, but this general setup is working great for me with production, staging and development environments.

Related

How to inject environment variables in Vercel Non-Next.js apps?

I deployed a regular React app to Vercel and would like to have a API_BASE_URL environment setting with different values for development, preview and production environments.
Typically, I'd use dotenv npm package with my webpack config setting up the variables for local or production depending on the build by looking into env.local and env.production respectively.
The env.production would look like this:
API_BASE_URL=#{API_BASE_URL}
Then, I'd have my deployment pipeline replace all instances of #{___} with the respective values available in the pipeline.
However, the regular way in Vercel seems to be to make a Next.js app and the environment variables in the project will be automatically available in the process.env variable in the backend code.
Is there anyway for us to have environment variables in a non-Next.js in Vercel?
I'm thinking I have 2 ways forward:
There is actually an easy way 🍀
Create my own build and deployment pipeline to replace the #{___} placeholders and deploy using vercel deploy command.
Had a similar issue on my SPA (React + Webpack) app on deployed on Vercel. This is what worked for me.
https://github.com/mrsteele/dotenv-webpack#properties
webpack.config.js
const Dotenv = require('dotenv-webpack');
module.exports = {
// OTHER CONFIGS
plugins: [
new Dotenv({
systemvars: true
})
]
};
Doing it like this I was able to read env vars from:
Vercel build runtime (ex: VERCEL_ENV)
.env file in my src folder
Env variables defined in Vercel console
Initially, I was overlooking an important fact. Vercel deploys builds and deploys changes in the same step.
2 commits in preview branch which are merge them both to main in 1 single commit: that's a totally new thing (a new build is created for every single commit in every environment).
This goes against the build once, deploy to multiple environments principle but I suppose Vercel's team considered that in their quest to make the development process as simple as possible.
Anyway, I worked out 2 solutions:
In the build command, insert the environment variables e.g. in the package.json:
scripts: {
"build": "webpack --env production --env VERCEL_ENV=$VERCEL_ENV"
}
Create my own build and deployment pipeline and then deploy to vercel using vercel deploy command. It also works but I think I'm going to stick to the option for simplicity, at least in the meantime.

Do you need to re-build a ReactJS app if you want to configure for a different environment

We have an app that is deployed to an Azure Web App
It's a create-react-app project.
My question is, when wanting to configure for a particular environment eg. API URLs, do you need to replace the env file and run "yarn build" again?
I am noticing that certain fields such as process.env.REACT_APP_API_URL are getting baked when building so I am thinking that a re-build is needed when needing to re-configure.
Is there a better way to pull configuration in a react app that doesn't require a re-build?

How to set .env for react app deployed with azure devops pipeline on app service

I developed a pipeline with CI/CD on azure Devops for deploying a React app on Azure web app service. Locally I'm using a .env file and this file is on .gitignore. I want to know how can i set the .env for reading it on production.
You can check the documentation below:
https://create-react-app.dev/docs/adding-custom-environment-variables/#adding-development-environment-variables-in-env
.env files should be checked into source control (with the exclusion of .env*.local ).
If you don't want to check in the .env files to DevOps, you could add the variables in the pipeline variables:
In addition, you can refer to the following case for more suggestions:
How to use environment variables in React app hosted in Azure
Many of the proposed solutions related to this issue may not work but I solved it the following way. However, first let me explain why other solutions may not (should not) work (please correct me if I am wrong)
Adding pipeline variables (even though they are environment variables) should not work since a react app is run on the client side and there is no server side code that can inject environment variables to the react app.
Installing environment variable task on the classic pipeline should not work for the same reason.
Adding to Application Settings in azure app service should not work for the same reason.
Having .env or .env.development or .env.production file in a git repo should not be a good practice as it may compromise api keys and other sensitive information.
So here is my solution -
Step1: Add all those .env files to azure devops library as secure files. You can download these secure files in the build machine using a DownloadSecureFile#1 pipeline task (yml). This way we are making sure the correct .env file is provided in the build machine before the task yarn build --mode development in the pipeline.
Step2:
Add the following task in your azure yml pipeline in appropriate place. I have created a github repo https://github.com/mail4hafij/react-yarn-azure-pipeline if you want to see a complete example.
# Download secure file from azure library
- task: DownloadSecureFile#1
inputs:
secureFile: '.env.development'
# Copy the .env file
- task: CopyFiles#2
inputs:
sourceFolder: '$(Agent.TempDirectory)'
contents: '**/*.env.development'
targetFolder: '$(YOUR_DEFINED_PROJECT_ROOT_FOLDER_VARIABLE)'
cleanTargetFolder: false
Keep note, secure files can't be edited but you can always re-upload.

Using create-react-app hosted on S3 to hit a Heroku API

Backgroud
The backend is a Rails API-only app running on Heroku (on port 5000 during local development).
The frontend is made with create-react-app and is hosted in an S3 bucket behind AWS CloudFront (on port 3000 during local development).
Development Setup
Locally, the frontend's package.json includes:
"proxy": "http://localhost:5000",
This lets me run a Javascript command like…
await fetch(`/someEndpoint`)
…and the development server running on localhost:3000 forwards the response from http://localhost:5000/someEndpoint. This works great and my frontend processes the responses successfully 👍.
Production Issues
Now I'm running on production.
If I change nothing, the same line will try to load from https://asdfasdfasdf.cloudfront.net/someEndpoint which will 404.
Instead I want to configure the app so that in production it will hit the Heroku app https://my-awesome-rails-app.herokuapp.com/someEndpoint where the Rails API is running.
Questions
(1) Is what I want possible without changing the code
await fetch(`/someEndpoint`)
either by configuring the frontend to use my-awesome-rails-app.herokuapp.com, or by configuring CloudFront to forward some requests to S3 and others to Heroku?
(2) If neither are possible, how should I write the fetch() command to let me hit Heroku in production but still keep the local proxying behavior during local development? Some use of environment variables maybe?
In my opinion, It's better to follow the same process (to get the API hostname) in all the environments. Also setting up CloudFront only for this purpose is not convincing unless you have other reasons to do so.
I think you could use an environment variable and here is how you can do it.
using environment variables in react project
react-scripts supports environment variables using dotenv library.
we build the react app using:
REACT_APP_API_DOMAIN=REACT_APP_API_DOMAIN npm run build
or you can define a .env.production where the NODE_ENV environment variable is set to production.
REACT_APP_API_DOMAIN=http://localhost:5000
Then in your code file, you can access it from process.env
await fetch(`${process.env.REACT_APP_API_DOMAIN}/someEndpoint`)
Please read the reference, there is a pattern using which you should define the environment variables for it to work. for e.g each environment variable you define should start with REACT_APP_
Reference:
https://create-react-app.dev/docs/adding-custom-environment-variables/
hope this helps.

Create-React-App + Heroku: Development, Staging and Production environments

I'm developing an app (front-end app that consumes an API) based on the create-react-app package. I'm using Heroku to deploy and currently have two deployments based on the same codebase, staging and production. These deployments should use different development/staging/production APIs that have different databases.
Is it possible to tell create-react-app to use different env variables based on how I run react-scripts start?
env.development
REACT_API: https://localhost/react_api
env.staging
REACT_API: https://myappstagingapi.heroku.com
env.production
REACT_API: https://myappproductionapi.heroku.com
How would I do this? And is this a good workflow?
Thank you very much!
I had the similar situation having more environments than production and development while deployment was done automatically from Github.
First of all, make sure you are using the deployment buildpack i.e.
https://github.com/mars/create-react-app-buildpack.git
You can add this URL in Settings in your project on Heroku, replacing NodeJS default buildpack for instance.
Full documentation is here:
https://elements.heroku.com/buildpacks/nhutphuongit/create-react-app-buildpack
If you follow the link above, you can see the chapter Environment variables. So, in order that your React App can process custom variables:
Add a one that suits you with REACT_APP_ prefix to your Heroku project environment variables (through Settings in Heroku Dashboard) Note that only the envs with this prefix will be visible in your process.env later so you should amend your code accordingly
Redeploy the application.
This should make the things work. I am sure it is also possible to keep .env file but I prefer to have more control via Heroku Dashboard.

Resources