This post was updated on 2020-12-09

A recurring foundational step in many front-end projects is to have a dynamic way of setting environment variables based on the type of build. In this post I’ll cover my approach to handling environment variables in a Stencil project. This is by no means the best or only way of doing this. Just the best approach that worked for me based on a number of other articles and projects I’ve seen in the wild.

First Steps

For my project, I wanted a way to reliably get the current environment I was building against, as well as have a simple solution to retrieve resource constants which could be different in each environment.

For instance, the canonical url for my api endpoint will be different in each deployed environment.

https://dev.webapp.com/api
https://webapp.com/api

To handle this let’s first create a resources.ts file in app/global that holds these urls.

const BASE_URL = 'webapp.com/api';

const resources = {
  dev: {
    users: `https://dev.${BASE_URL}/user`,
  },
  prod: {
    user: `https://${BASE_URL}/users`,
  }
};

export const apis = resources;

So now we can use our api endpoint from any module like:

import { apis } from 'resources'

fetch(apis.prod)
  .then((response) => {
    return response.json();
  })
  .then((users) => {
    console.log(users);
  });

What we really want is to have apis.<env> already set for us. Here’s where the Env variable in @stencil/core comes in handy.

Env Variables from stencil.config

As of @stencil/core@2.3.0, there is an Env variable available in Stencil out of the box which you can import directly.

Going back to app/global/resources.ts, let’s add this Env import. Then change the export to use this variable.

import { Env } from '@stencil/core';

const BASE_URL = 'webapp.com/api';

const resources = {
  dev: {
    user: `https://dev.${BASE_URL}/users`,
  },
  prod: {
    user: `https://${BASE_URL}/users`,
  }
};

export const apis = resources[Env.apiEnv];

To get Env assigned the correct value, we need to set it in stencil.config.ts.

The first part is easy since we’re just using node.

const dev: boolean = process.argv && process.argv.indexOf('--dev') > -1;
const apiEnv: string = dev ? 'dev' : 'prod';

Then in stencil.config.ts we use the env Config property to set the api environment.

import { Config } from '@stencil/core';

const dev: boolean = process.argv && process.argv.indexOf('--dev') > -1;
const apiEnv: string = dev ? 'dev' : 'prod';

export const config: Config = {
  // ...
  env: {
    apiEnv: apiEnv
  }
  // ...
};

Now when running our build scripts the whole process can be seen in action. The --dev flag will control which set of constants get exported in resources.ts

stencil build --dev

So now we can use our api endpoint and it will have the correct env property exported:

import { apis } from 'resources'

fetch(apis.user) // will be https://dev.webapp.com/api/users
  .then((response) => {
    return response.json();
  })
  .then((users) => {
    console.log(users);
  });

This post shows a very basic example of api strings in only two environments. The strings in resources.ts could be anything useful to the app not just api endpoints. And the environments could match whatever makes sense for your project.