DEV Community

Jonathan Flower
Jonathan Flower

Posted on • Updated on

Deploy Angular Universal to Multiple Environments Using Node ENV Vars

Deploying Angular and especially Angular Universal using CI to a staging and a production environment is very difficult. A quick Google search reveals a lot of really ugly solutions. Here is a cleaner solution:

The root of the issue is how the Angular CLI decides which angular environment to use based on a command line variable.

ng build --configuration=production Will build the application with the production environment variables set in environments/environment.prod.ts. How do you get a production Heroku server to build with ng build --configuration=production and a staging server to build with ng build --configuration=staging?

Heroku now automatically calls npm install and npm build. Have your npm build script call: node build-app.js

const { exec } = require('child_process');
console.info(
    'node env var ANGULAR_ENVIRONMENT_CONFIGURATION',
    process.env.ANGULAR_ENVIRONMENT_CONFIGURATION
);

let buildScript = 'npm run build:staging:ssr';
if (process.env.ANGULAR_ENVIRONMENT_CONFIGURATION === 'production') {
    buildScript = 'npm run build:prod:ssr';
}
const child = exec(buildScript, function (err, stdout, stderr) {
    if (err) throw err;
    else console.info(stdout);
});

child.stdout.on('data', function (data) {
    process.stdout.write(data);
});

child.stderr.on('data', function (data) {
    process.stdout.write(data);
});

child.on('exit', function (data) {
    process.stdout.write("I'm done!");
});
Enter fullscreen mode Exit fullscreen mode

The idea here is that this node app is able to read node environment variables and then decide on which angular build command to call.

In Heroku, set the node environment variable ANGULAR_ENVIRONMENT_CONFIGURATION to either staging or production appropriately.

This is pretty much all you need to know for deploying Angular. Take a look below at the npm build scripts and simply omit the :ssr and ng run my-app:server:production. There are plenty of other tutorials out there that demonstrate how to add staging to the angular.json file. Here are a few:
https://tattoocoder.com/angular-cli-using-the-environment-option/
https://blog.angulartraining.com/how-to-manage-different-environments-with-angular-cli-883c26e99d15

Angular Universal

If you are using Angular Universal it will always default to building the pre-render files with the production environment. You have to add some additional configuration in order to get it to pre-render with the staging environment. The key is to setup your npm build scripts like so:

"build:prod:ssr": "ng build --configuration=production && ng run my-app:server:production",
"build:staging:ssr": "ng build --configuration=staging && ng run my-app:server:staging",
Enter fullscreen mode Exit fullscreen mode

And update your angular.json

 "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/my-app/server",
            "main": "server.ts",
            "tsConfig": "tsconfig.server.json"
          },
          "configurations": {
            "production": {
              "outputHashing": "media",
              "sourceMap": false,
              "optimization": true
            },
            "staging": {
              "outputHashing": "media",
              "sourceMap": false,
              "optimization": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.staging.ts"
                }
              ]
            }
          }
        },
"serve-ssr": {
          "builder": "@nguniversal/builders:ssr-dev-server",
          "options": {
            "browserTarget": "my-app:build",
            "serverTarget": "my-app:server"
          },
          "configurations": {
            "production": {
              "browserTarget": "my-app:build:production",
              "serverTarget": "my-app:server:production"
            },
            "staging": {
              "browserTarget": "my-app:build:staging",
              "serverTarget": "my-app:server:staging"
            }
          }
        },
        "prerender": {
          "builder": "@nguniversal/builders:prerender",
          "options": {
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {
              "browserTarget": "my-app:build:production",
              "serverTarget": "my-app:server:production"
            },
            "staging": {
              "browserTarget": "my-app:build:staging",
              "serverTarget": "my-app:server:staging"
            }
          }
        }
Enter fullscreen mode Exit fullscreen mode

Discussion (0)