Heroku pipeline - staging env variable carried into production

4.8k Views Asked by At

I've recently deployed a React/Express app to Heroku, but I'm having an issue with environment variables that are part of the built app and the Heroku deployment pipeline - in a nutshell, the values of environment variables from the app's staging release are being carried over when promoting to production - the only way that I can get the environment variables to set correctly is to push the app directly to production, which really defeats the purpose of the deployment pipeline in the first place. Here's a summary of the scenario:

The environment variable in question is API_URL, which is referenced in webpack.config.js like so:

plugins: [
    new webpack.DefinePlugin({
        'API_URL': JSON.stringify(process.env.API_URL || 'http://localhost:4000/api/v1')
    })
]

The API is itself another Heroku app with staging and production releases, and so the values of the API_URL environment variable are set in my React app Heroku configuration as https://staging-api-12345.herokuapp.com/api/v1 and https://production-api-12345.herokuapp.com/api/v1, respectively.

When I push my React app up to staging, it works perfectly - however, when I promote the app to production and make the first call to the API, it is still pointing to https://staging-api-12345.herokuapp.com/api/v1. Ok well, I understand why that is the case - the app was built when being pushed to staging... so I tried rebuilding the app after promoting to production, but that did not work, it still used the staging environment variables.

When using the Heroku deployment pipeline, is there a way to force the app slug to rebuild so that it will catch the different environment variables?

2

There are 2 best solutions below

3
On BEST ANSWER

You cannot rebuild the slug, the main point of pipelines is to move the same slug between apps.

What you need to do is to fetch API_URL at runtime and not during build process. You can put all envs in one file, for example env.js

export const API_URL = process.env.API_URL;
export const OTHER_VAR = process.env.OTHER_VAR;

And then just import what you need in other files

import { API_URL, OTHER_VAR } from 'env.js';
0
On

There might be situations where you need env vars during build time for different environments further downstream the pipeline. A setup like this for example:

Test |--> Prd Europe
     |--> Prd USA

Say, for SEO reasons, you want to have a different title for USA by using the env vars and it should be immediately available in the template, not after some miliseconds. This is in a pipeline not possible since the slug is built at test where only a single tenant can be served, and async loading is too slow. Some crawler may pick the placeholder title instead for example.

A workaround for this, besides not using a pipeline altogether, could be to generate multiple templates per environment (at build time). In this case two templates europe.html and usa.html. Then serve them based on the env vars at runtime by a web server. So if REGION === 'USA', serve usa.html with the env vars baked in at the test env.