Hide environment variables in your StencilJS project
In a previous article I described how I implemented environment variables in my Stencil’s projects or in my projects created with the Ionic PWA toolkit.
Since this publication, we began to work actively on our upcoming editor for PWA presentations, DeckDeckGo, and as the project is open source and published on Github, we thought that it would maybe be a not too bad idea to not publish our API keys and other Firebase tokens online in our public repo 😉
For that reason, I had to go a step further than my previous post respectively I had to find and develop a solution to save separately our keys. Therefore I’m happy to share my solution with this new blog post 😃
Side note
Yes, we are aware that our environment variables will be contained in our client bundle and therefore exposed to anyone with a bit of retro engineering but we thought that it would be a bit cleaner to not push them in our Git repo.
Getting started
Stencil’s projects relies on Rollup, therefore we are not going to reinvent the wheel, we are just going to use the Rollup’s plugin rollup plugin-replace which we install running the following command line:
npm install rollup-plugin-replace --save-dev
This plugin will allow us to replace values at bundle time.
Configuring the environments
Once the plugin installed, we could edit our configuration in our bootstrap classes respectively we could edit our app.ts
file in order to replace the values we want to hide. In this specific post we are going to hide the url of the API. To do so, we are going to replace the values with a unique selector (which will be automatically replaced with the real values at bundling time once we have implemented the all solution):
import {setupConfig} from
'../app/services/environment/environment-config';
setupConfig({
url: '<@API_URL@>',
production: true
});
In my previous article we defined two different environments and therefore I will still assume that our goal is to handle two environments, respectively production
and development
.
Identically we are then going to replace the API url with our selector in the development configuration, respectively in the app-dev.ts
file:
import {setupConfig} from
'../app/services/environment/environment-config';
setupConfig({
url: '<@API_URL@>',
production: false
});
Note that we are using <@
and @>
to make our selector really unique. These attributes aren’t static, if you rather like to use others it’s absolutely up-to you, no problem. You should then just reflect later in this tutorial your modifications when you will configure the plugin.
Defining the selectors’ values
To finalize the configuration, we should now create “somewhere” some files which will contain the real values for our selectors. Personally, I have chosen a solution where I handle the values in json
files, placed at the root of my project and excluded from Git (I have added them to the list of Git .ignored
files). We could therefore for example create a new file config.prod.json
at the root of the project and edit it with our selector and real values:
{
"API_URL": "https://api.production.com"
}
Of course, we should also now create another configuration file config.dev.json
for the selector and values or our development environment:
{
"API_URL": "http://localhost:3002"
}
Running the application
Now that our configuration is ready, we should just configure and use the plugin we installed before in order to replace the selector with the real values at bundle time. For that purpose we are going to edit stencil.config.ts
like the following:
import replace from 'rollup-plugin-replace';
// See my previous article for development detection
const dev: boolean = process.argv && process.argv.indexOf('--dev') > -1;
import devConfig from './config.dev.json';
import prodConfig from './config.prod.json';
const configValues = dev ? devConfig : prodConfig;
export const config: Config = {
...
plugins: [
replace({
exclude: 'node_modules/**',
delimiters: ['<@', '@>'],
values: configValues
}),
...
};
Voilà, nothing more, nothing less 🎉 From now on, each time you application will be bundled, the configuration values you saved separately or you didn’t committed in your repo will be injected at bundle time, easy peasy 👻
Cherry on the cake 🍒🎂
Like I said in my introduction, this solution is implemented in our upcoming editor for PWA presentations DeckDeckGo and, as this project is open source, you are most welcomed to have a look at the solution I have implemented. You will notice that I implemented the exact same code and configuration as above but you will not find any config.prod.json
or config.dev.json
in our repo 😉
https://github.com/deckgo/deckdeckgo/tree/master/studio
To infinity and beyond 🚀
David
Discussion (8)
I was looking for some articles on this and I ran into
rollup-plugin-dotenv
which allows one to useprocess.env.YOUR_VARIABLE
. Do you have any thoughts on this? From my understanding, it usesrollup-plugin-replace
behind the scenesI did not knew about
rollup-plugin-dotenv
. Is that this repo or another one? The README is empty.Anyway, sure if there is such a plugin which would allow the usage of
.env
variables, that would be super too!Yeah, that's it. Although I'm a bit curious about how it would work when generating static site using Stencil Js
Sure me too. If you give it a try, let me know how it went!
Thank you for the tutorial :)
I didn't get one thing...
Inside config.dev.json and config.prod.json there are no devConfig/prodConfig.
Infact,
import devConfig from './config.dev.json';
import prodConfig from './config.prod.json';
gives me this error "Cannot find module 'config.dev.json'. / Cannot find module 'config.prod.json'. "
The code inside the two json files it's the same of the tutorial.
You are facing the error in your editor or when you run the command?
In my editor I had to add the following:
but otherwise, do you have maybe a special
tsconfig.json
file which doesn't allow you to import from json files?is your project open source, could I check it out?
Hello, nice article but I am looking for a way to replace vars at delivery stage. In common I use an config.txt file with placeholders in an docker container. At launch a shell script in the container replaced the placeholders depends on environment vars of container. In the container an nginx deliver the dist folder of webcomponent.
This method does not work for stencil because my config.txt was embedded into the compilat and renamed.
Currently workaround is to filter all *.js files of dist folder and replace the placeholder. In my mind this could be harmeful.
Do you have an idea to solve this problem?
Thanks
Thomas
Is it like one config for many components or just one? if just one, maybe you can add a
Prop()
to it an use it to inject the configuration according the environment where it is used?Alternatively, maybe read the configuration with a
fetch
from your component assuming that your configuration is present in the dist folder and that the path is know?Just spontaneous idea, not sure it fits well your use case though, let me know!