Backstage - How to consume environment variables? - reactjs

The Backstage documentation states that all environment variables must be "exposed" through the central configuration file, app-config.yaml.
However, the official documentation is not clear about the use of these variables, for example, in the .ts and .tsx files.
Could someone help, or exemplify with codes how this use is made?

Had the same question, ended up just using process.env.ENV_VAR in the typescript files and exporting environment variables before starting Backstage.

There's a standard configuration API for both frontend and backend plugins or codes. An API reference can be found here.
You can try something like:
import { Config } from '#backstage/config';
interface IBackendConfig {
KEY_1: string;
KEY_2: string;
KEY_3: string;
}
const getBackendConfig = (config: Config): IBackendConfig => {
return config.get<IBackendConfig>('backend.env');
}
In your app-config.yaml
backend:
env:
KEY_1: "value1"
KEY_2: "value2"
KEY_3: "value3"
Note: Because of this syntax, configuration keys cannot contain dots.
Another option for accessing the env value is to create a sub-view of the configuration,
config.getConfig('backend').getString('env').

Related

How to use i18next on a custom Typescript library and a host at the same time?

I am making a typescript library that accepts an object and spits out an excel. This library is supposed to be used with several React apps. Each React app[Host] will provide an object and the custom library returns an excel.
All the React apps use i18next for translation. The custom library also needs to use its own set of translations so that also has i18next. I am trying to avoid passing the translation strings from the Host to my library.
Problem:
As soon as I call any function in my library, i18next in the library takes over the i18n of Host. This is expected, As we have two i18next.init() functions [One in my library and one in the host]. Whoever gets called last wins [Library in my case]. All the translation strings in the Host are noted to be missing [by the i18nxt of library] since the newly initialized i18next in the library can't see Host translations.
Question:
How can I approach this problem, if I need to keep i18n on both the library and host? I am expecting both to have their own translations.
I believe you won't need any code to understand the problem.
As I have updated my question, I have managed to get this working. In my case, I have multiple react host apps which use my library. Some hosts don't have i18next installed and some already have it as a dependency. I needed a way to detect whether i8next is initialized or not. If it is not initialized initialize it with the excel generation library resources. Below is the code where I am detecting and adding/updating the resources based on the status of i18next. For my library resources, I am using a namespace so there won't be a name collition. You can access your library resources like t("tkdirectequibuild:heading"). I had to specify the namespace along with key always, which is not that bad. I kept lang as optional as i18next can detect the language in case of an already instantiated host.
import i18next from "i18next";
import enResources from "./translations/en/translation.json";
import frResources from "./translations/fr/translation.json";
export function addExcelResource(lang?: string) {
if (i18next.isInitialized) {
console.info("i18Next Exists");
if (!i18next.hasResourceBundle("en", "tkdirectequibuild")) {
console.info("en-Added");
i18next.addResourceBundle("en", "tkdirectequibuild", enResources);
}
if (!i18next.hasResourceBundle("fr", "tkdirectequibuild")) {
console.info("fr-Added");
i18next.addResourceBundle("fr", "tkdirectequibuild", frResources);
}
if (lang) {
i18next.changeLanguage(lang);
}
} else {
console.info("i18Next is missing in the HOST. Adding our own");
i18next.init({
lng: lang,
ns: ["tkdirectequibuild"],
initImmediate: false,
resources: {
en: {
tkdirectequibuild: enResources,
},
fr: {
tkdirectequibuild: frResources,
},
},
});
}
return i18next;
}
In the calling library function, I added these lines,
const i18n = addExcelResource(lang); const { t } = i18n;

Hiding secret key in webpack bundle.js from redux-persist-transform-encrypt

I am using redux-persist in combination with redux-persist-transform-encrypt to encrypt my redux store in localstorage. So I implemented the encryption like so:
const encryptor = createEncryptor({
secretKey: 'my-super-secret-key',
onError: function(error) {
// Handle the error.
console.log(error);
}
});
const persistConfig: PersistConfig = {
key: 'citizentracker',
storage: storage,
blacklist: new Array('form'),
transforms: [encryptor]
};
Everything works well and the redux store is encrypted in local storage. The issue I noticed is that when I do my production bundle via webpack 4. In the bundle.js file you can see the key value by searching for "secretKey". When I did I was able to see this:
{secretKey:"my-super-secret-key",onError:function(e){console.log(e)}}
Does anybody know of a way to generate a key to use for encryption, but also hide that key from people viewing the bundle.js in sources? Or some other way of making this encryption more secure.
So one thing I went and explored the option of doing was to use uglifyjs-webpack-plugin to use with webpack. This allowed me to do object property name minification via the mangle property of the UglifyJsPlugin. So in the following code, I told it to minify only the object property of "secretKey" via regex. The secret in plain text will still exist in the bundle.js file, but at least now it will be harder to find. Still exploring additional options.
This is the new webpack plugin added to the optimization property of the webpack config variable.
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
mangle: {
properties: {
regex: /secretKey/
}
}
}
})
]
},
So the output into bundle.js would become:
{u:"my-super-secret-key",onError:function(e){console.log(e)}}
This helps but of course would like to hear better solutions!

React and environment variables

I'm currently a bit confused about react and env variables. Basically, what I would like to achieve is, to have different files. Something like: enviorment.dev.js, enviorment.prod.js.
I couldn't find the documentation and there seem to be a lot of different options to choose from.
I guess, I look for something like this: https://medium.com/beautiful-angular/angular-2-and-environment-variables-59c57ba643be just for react.
If you're using webpack, there's a plugin, but that's the sort of place where you would need to configure that. As Felix said, React doesn't do that:
https://webpack.js.org/plugins/define-plugin/
webpack.config:
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})
index.js:
if (!PRODUCTION) {
console.log('Debug info')
}
if (PRODUCTION) {
console.log('Production log')
}

Location of static application settings

I'm developing a Redux/ReactJS application and I have a list of application settings.
I'm trying to decide if I should have them in the store or if I should create a file which contains the settings and import it where I need it.
It depends on the nature of the 'application settings'.
For the secure things
Like API keys, etc. Use environment variables. You can use something like dotenv to make simulating other environments easier.
For constants
Like strings and colors and external URLs, I use a constants file with multiple exports. Then in each module I import whatever I need, like so:
import {
ANIMATION_DURATION,
COLORS,
MODALS,
TEXT_PADDING,
} from '../../constants.js';
For environment-specific things (e.g. dev, prod ...)
For example API URLs or log levels, etc. use a set of config files. So you might have a config-dev.js and config-prod.js and then a config.js that returns the correct file's contents based on process.env.NODE_ENV
Application settings should be just a const in some file. In this way, for different configurations, the settings can be toggled.
An example of settings file could be:
let apiUrl = 'http://prodUrl';
// __DEV__ defaults to true in simulator.
if (__DEV__) {
apiUrl = 'http://devUrl';
}
export const settings = {
apiUrl,
registerUrl: `${apiUrl}/api/register`,
educationUrl: `${apiUrl}/api/education`,
};

Setting global variables in webpack for front-end config?

I have a different URL for our api depending if it's development or production for a react app.
Using webpack, how can I set an env var like __API_URL__ and then change that depending if it's built using webpack.config.dev vs webpack.config.prod
I thought the answer may be in webpack.DefinePlugin but no luck.
new webpack.DefinePlugin({
__API_URL__: 'localhost:3005',
}),
I'd expect __API_URL__ to be available as a global but no such luck.
What would be the right way to do this? Also key thing is that no express server on the prod deploy. So this has to happen during the build...
As Michael Rasoahaingo said, the DefinePlugin works similar like replacing values with regular expressions: It replaces the value literally in your source code. I would not recommend to use the DefinePlugin for this kind of task.
If you want to switch configs based on the environment, you could use resolve.alias for that. Just import your config like this:
var config = require("config");
and then add a mapping in your webpack.config.js:
resolve: {
alias: {
config$: require.resolve("path/to/real/config/file")
}
}
DefinePlugin is not working as you expected. It doesn't expose __API_URL__ as a global variable.
According to the documentation: "The values will be inlined into the code which allows a minification pass to remove the redundant conditional."
So, it will find all occurence of __API_URL__ and changes it.
var apiUrl = __API_URL__
and
__API_URL__: '"localhost:3005"' // note the ' and "
become
var apiUrl = "localhost:3005"

Resources