In Prod

← Back to Posts

Configuration variables in SPFx

February 11, 2020

Thank you to Sergei Sergeev for the info.

A common problem that developers have is needing to have a set of values for testing locally that are different to those in production. For example, an API URL that targets a local server in development, and the live one in production. Being able to separate the values automatically depending on the current working environment can help speed up development without worrying about making any accidental/undesired changes to production data.

In SPFx we can use our gulpscript.js and webpack to detect what environment we are in and inject different values accordingly.


Setup

For this method to work we need to install webpack-merge and cross-env to help merge our new webpack config and inject environment variables for run time.


_1
yarn add webpack-merge cross-env

Once installed, we need to add a new command to our serve script:

package.json

_8
"scripts": {
_8
"serve": "cross-env NODE_ENV=DEVELOPMENT gulp serve --nobrowser",
_8
}
_8
_8
"devDependencies": {
_8
"cross-env": "^6.0.3",
_8
"webpack-merge": "^4.2.2"
_8
}

The new script uses cross-env to inject the NODE_ENV value for dev purposes. So when we start the debugging process, by running yarn serve, the value will be set. In production, the value won't be changed and will run in production mode by default.


Config

We now need to change our gulpscript.js to look for this flag at build time and inject the specific values we require.

In the below example we inject a single apiUrl that is different between environments:

gulpscript.js

_34
'use strict';
_34
_34
const gulp = require('gulp');
_34
const build = require('@microsoft/sp-build-web');
_34
const merge = require('webpack-merge');
_34
const webpack = require('webpack');
_34
build.addSuppression(/Warning - \[sass\] The local CSS class .* is not camelCase and will not be type-safe./gi);
_34
_34
build.configureWebpack.setConfig({
_34
additionalConfiguration: (generatedConfiguration) => {
_34
let isDevelopment = process.env.NODE_ENV === 'DEVELOPMENT';
_34
let defineOptions;
_34
if (isDevelopment) {
_34
console.log('*** DEV ENV ***');
_34
// specify development keys here
_34
defineOptions = {
_34
'apiUrl': JSON.stringify('https://localhost:3000')
_34
}
_34
} else {
_34
// specify production keys here
_34
defineOptions = {
_34
'apiUrl': JSON.stringify('https://myproductionapi.com')
_34
}
_34
}
_34
_34
return merge(generatedConfiguration, {
_34
plugins: [
_34
new webpack.DefinePlugin(defineOptions)
_34
]
_34
});
_34
}
_34
});
_34
_34
build.initialize(gulp);


Accessing the variables

Now that we have our apiUrl set, we need a way to access it in code. To do this we can set up a helper class that exposes the variables:

HostSettings.ts

_7
declare var apiUrl: string;
_7
_7
export class HostSettings {
_7
public static get ApiUrl(): string {
_7
return apiUrl;
_7
}
_7
}

And then use in code:

index.tsx

_6
import HostSettings from "./HostSettings";
_6
_6
console.log(HostSettings.ApiUrl());
_6
_6
// dev output: https://localhost:3000
_6
// prod output: https://myproductionapi.com


Andrew McMahon
These are a few of my insignificant productions
by Andrew McMahon.