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

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.

Related

Gatsby - Build website for stage/development - which command should I use?

How can I build/deploy a gatsby website for development environment?
If you run gatsby build the environment variable is automatic set to production.
Use Case: I do want to deploy my website to services such as Netlify, etc, but I want to BUILD for STAGE environment. Anyone know which command I can use for that or if I should replace the NODE_ENV in the gatsby build command? I want to deploy and deliver the staging website to the coworkers/users/customers, then they will be able to test and use it on the staging environment. (with all staging configuration). If you are developing a website for customers you might need to deploy a staging version for test/validation purposes.
I know that gatsby/react internally have some different processing based on the environment, but I was wondering if it's possible to build and deploy a gatsby website for development/stage purposes using development variables configuration.
An example: I was using two plugins - gatsby-google-tag-manager and gatsby-plugin-facebook-pixel and realized that both configurations relies on NODE_ENV development or production and I currently can not build and deploy a website using development environment.
Any thoughts on that?
gatsby build overrides NODE_ENV environment variable.
I solved it by using another environment variable to override NODE_ENV in gatsby-config.js
At the top of the filegatsby-config.js:
if (process.env.DEVELOPMENT === 'TRUE') {
process.env.NODE_ENV = 'development'
}
require ('dotenv'). config ({
path: `.env. $ {process.env.NODE_ENV}`,
})
// etc...
Run development build:
DEVELOPMENT=TRUE gatsby build

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

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.

How should I integrate Storybooks with React, Webpack, Docker, lighttpd?

I'm working on a Dockerized react app. It is built with Webpack 4. As part of the CI process on this project, the app is deployed to both staging and production environments, where it's served with lighttpd. What I would like to do is have a Storybooks page that is accessible on the staging site, but not on prod.
There are a few issues here that are making it hard for me to figure out the best approach.
As part of the CI process the app is built and automatically deployed to the staging environment when a commits are pushed to any git branch with a name of the form staging/...
The switches that configure the app for staging vs. prod are primarily in the project's webpack config.
That build process is managed by a dockerfile which I can edit. I can run yarn commands (to build a static storybooks bundle, e.g.) in that dockerfile. But the same dockerfile is used for both staging and prod and I'd prefer not to have to create a separate one.
I would prefer not to have to change the react-router routes at all, because the routes file should (ideally) be the same for staging and prod.
I'm generally a little fuzzy on how/whether a user could enter a URL on the staging environment that bypasses react-router somehow and shows the static storybooks page. The networking aspects of web dev are not my strong suit. I'm usually just building and styling components.
My approach thus far has been to simply build a static storybooks bundle by adding a RUN yarn storybooks line to the dockerfile, but this fails in a couple of ways. 1) It runs for both staging and prod.
2) It seems to build the static storybooks bundle (i.e. the dockerfile output shows the storybooks building via it's own, internal webpack process successfully), but I'm not sure how to actually view it in the browser.
I hope that all makes some sense. What I'm looking for is general "best practices"-style guidance. Please let me know if I can provide any additional info and thanks for the time.
An easy way would be to RUN yarn storybooks as a separate process using concurrently in package.json script named prod and run the right command by using a --build-arg in dockerfile
in your Dockerfile
#EXEMPLE docker build --build-arg running_env=preprod
ARG buildenv
RUN yarn run $buildenv
Do not forget to expose the right ports ... my script in package.json looks like this :
"storybook": "start-storybook -c storybook/ --ci -p 3007"
and as for the routing you could add a link and redirect to the storybooks page. I am currently figuring this out too.

Heroku Pipeline with Angular 2 Environments

I've built my Angular 2 project using the Angular 2 CLI and was able to deploy the application to Heroku using this tutorial.
Now, I'd like to create a pipeline for the different environments of the applications (dev, staging, production, etc...).
In my package.json I have "postinstall": "ng build -prod" which creates a production build of my code, which my application runs off of. Is there a way that I could have -prod change based on CONFIG_VARS that I would have in my Heroku setup? For example it would say "postinstall": "ng build -dev" for a dev environment or "postinstall": "ng build -staging" for a staging environment. Or would I need to set up my project differently?
The short answer is: No, you will need to do something different.
Explanation:
The npm postinstall script runs when the Heroku slug is built, when you do a git push to the first Heroku app in your pipeline. Subsequently, when you "promote" releases through your Heroku pipeline (e.g. from "development" to "staging" to "production"), the pre-built Heroku slug is promoted "as is", and is NOT rebuilt.
Hence, let's say you have a config var set up in your "development" app that would set the argument that you pass to "ng build" to "dev". That means that when you git push to your "development" app, the slug will get built with the "dev" options. That is fine for the "development" app. HOWEVER, when you subsequently promote to "staging" and "production", you will be promoting the pre-built slug that was built with the "dev" options, which is NOT what you want.
So, to get the functionality you want you will need to consider a different approach.
One way to do it would be to run your "ng build" script in the "npm prestart" phase. That should work, and enable you to use Heroku config vars to modify your Angular2 app depending on the Heroku pipeline phase they are deployed on. However, I typically would NOT recommend this approach. This will cause your "ng build" to run every time "npm start" is run, which is quite often on Heroku (i.e. at least once every 24 hours or so, plus every time your Heroku dynos restart for whatever reason). This would cause your app to experience longer downtime than necessary EVERY time your dynos restart. Not a good idea, typically.
Instead, a better option may be to have your Angular2 app query your server when it initializes, and have the server return whatever pipeline-phase specific values your Angular2 app needs, based on regular Heroku config vars.
If you are running your application in a Heroku node environment you can try to take a look at this solution to avoid having your environments variables and security keys hardcoded in the repositories: https://medium.com/#natchiketa/angular-cli-and-os-environment-variables-4cfa3b849659
I strongly suggest also to take a look at its first response that introduce a dynamic solution to create the environment file at building time: https://medium.com/#h_martos/amazing-job-sara-you-save-me-a-lot-of-time-thank-you-8703b628e3eb
Maybe they are not the best solutions, but a trick to avoid setting up projects differently each time.
i found your question when i was researching the same issue.. how to use environment vars from heroku inside of angular..
after not finding any satisfying answer i came with this (cookie) approach for using different API endpoints for my pipeline.
set a cookie in your server response (server.js) with the content of your var:
app.use(function(req, res, next) {
res.cookie('API_URL', process.env.API_URL || 'http://127.0.0.1:8500/api/');
next();
});
read the value inside of your module:
this.apiUrl = cookieService.get('API_URL');
it's a workaround that will of course only work when user accepts cookies...

Angular app on Heroku built using Webpack - Environment variables?

I am attempting to figure out the best way to load environment variables into my AngularJS app. I am currently using constants, which take their valuues from those defined in the Webpack definePlugin. However, this causes an issue with Heroku as the code is built when pushed to staging, and when it is promoted to production, it is not rebuild, therefore the webpack definePlugin constants are the staging environment variables.
I have looked at requesting the environment variables from my API at run time and then setting them as constants to be used in my front-end, but I can't figure out how to set constants programatically outside of the initial .constant(..) opportunity.
If anyone knows any other better practices around loading environment variables into a front-end when using Webpack (and not Grunt) please do let me know.
If you are using node.js (and npm) in your server, you could consider running webpack in the "npm prestart" script, instead of in "npm postinstall".
That way webpack would run every time your heroku dynos start or recycle, and so would pick up your env var definitions from the appropriate Heroku pipeline phase. So, when your staging dynos start, webpack would pick up your staging env var definitions, and when your production dynos start, webpack would pick up your production env var definitions.
The disadvantage of this approach, however, is that increases the time your dynos are out of service while recycling, since they now need to run webpack before starting.

Resources