DEV Community

loading...

Hide environment variables in your StencilJS project

David Dal Busco
Creator of DeckDeckGo | Organizer of the Ionic Zürich Meetup
Updated on ・4 min read

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
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
      }),
    ...
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

To infinity and beyond 🚀

David

Discussion (8)

Collapse
newtonmunene_yg profile image
Newton Munene

I was looking for some articles on this and I ran into rollup-plugin-dotenv which allows one to use process.env.YOUR_VARIABLE. Do you have any thoughts on this? From my understanding, it uses rollup-plugin-replace behind the scenes

Collapse
daviddalbusco profile image
David Dal Busco Author

I 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!

Collapse
newtonmunene_yg profile image
Newton Munene

Yeah, that's it. Although I'm a bit curious about how it would work when generating static site using Stencil Js

Thread Thread
daviddalbusco profile image
David Dal Busco Author

Sure me too. If you give it a try, let me know how it went!

Collapse
jericam profile image
Erica Marchi • Edited

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.

Collapse
daviddalbusco profile image
David Dal Busco Author

You are facing the error in your editor or when you run the command?

In my editor I had to add the following:

// @ts-ignore
import devConfig from './config.dev.json';
// @ts-ignore
import prodConfig from './config.prod.json';

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?

Collapse
huluvu424242 profile image
Huluvu424242 • Edited

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

Collapse
daviddalbusco profile image
David Dal Busco Author

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!