DEV Community

loading...
Cover image for Accessing Environment Variables in an Easier Way

Accessing Environment Variables in an Easier Way

Christopher Daniel
Full-Stack Engineer and bird dad.
・4 min read

In server-side development, it's extremely common to access variables from the execution environment.

In this post, I hope to convince you to consolidate access of these variables into one single-file (or a more structured way of accessing these values) to make it easier to refactor, maintain, and update as your project grows.

// Logging to console the stage we're on
console.log(`This is the ${process.env.NODE_ENV}`);
Enter fullscreen mode Exit fullscreen mode

Why is it useful to access variables from the environment?

I won't dive too much into the why of this, but you typically access sensitive values via this way, such as

  • API keys and secrets
  • Application identifiers
  • Stage of the environment (looking at you NODE_ENV)
  • JSON Web Token key
  • Database access credentials
  • other top-secret values of this nature

These are values that you do not want committed to a version control system like GitHub and so you keep them out of there for security purposes.

image

You might also keep these out of there because these vary from stage to stage, and thus makes no sense to keep in GitHub.

So, getting them during runtime of the program it is! 😃

What's the issue with process.env?

In your own projects, you might be accessing environment variables through process.env.MY_VARIABLE. This is great! It's fine, and it works.

But is it optimal?

Imagine you have two files that access the same environment variable, some sort of API key

// Usage 1
axios({
  url: `${process.env.CMS_URL}/random-endpoint-1`/v1/random-endpoint-1`
  header: `Bearer ${process.env.MY_API_KEY}`
});

// ...

// Usage 2
axios({
  url: `${process.env.CMS_URL}/random-endpoint-1`/v1/random-endpoint-2`
  header: `Bearer ${process.env.MY_API_KEY}`
});

Enter fullscreen mode Exit fullscreen mode

Both of these files are accessing the same API key from the environment directly. Now imagine your projects expands in scale and you have many more instances where this API key needs to be accessed.

See the problem that might occur? You now would process.env.MY_API_KEY littered throughout your project.

What if you need to change the environment variable from process.env.MY_API_KEY to process.env.TWITTER_API_KEY?

  • Yes, you can easily rename all instances (using a powerful editor like VS Code). But this is going to cause a pretty large commit created for this simple change.

What if you have a plethora environment variables, and you want to group them? Like API credentials, database credentials, etc.?

  • There's no way to do this with the normal process.env.XXX_YYY usage. Everything is at the same level and there's no grouping them.

What if you want to add context to each environment variable, so engineers can understand what purpose they serve?

  • You can do this in your .env.template file as single-line comments, but this won't show up in the IDE as a hint or documentation for your team members.

How should we be accessing the environment variables?

I won't say you 100% definitively, absolutely, should follow my advice. But I think it can help prevent the above shortcomings (and also add to your current environment variable usage).

Add a config.js or config.ts file!

What do I mean?

I mean consolidate access of environment variables from using process.env.XXX_YYY everywhere, to just accessing it once! Through a single file(s)!

It can look something like

export const Config = {
    cmsUrl: process.env.CMS_URL,

    dbHost: process.env.DB_HOST,
    dbUser: process.env.DB_USER,
    dbPassword: process.env.DB_PASSWORD,
    dbName: process.env.DB_NAME,

    jwtSecret: process.env.ZEROCHASS_SECRET,

    awsRegion: process.env.AWS_REGION,
    awsBucket: process.env.AWS_BUCKET,

    twitterApiKey: process.env.TWITTER_API_KEY,
}
Enter fullscreen mode Exit fullscreen mode

Now, whenever I want to access any of these environment variables, I can do so by importing this file.

No more having to write process.env.MY_VARIABLE over and over!

My above example with axios becomes this

import { Config } from './config';

// Usage 1
axios({
  url: `${Config.cmsUrl}/random-endpoint-1`
  header: `Bearer ${Config.twitterApiKey}`
});

// ...

// Usage 2
axios({
  url: `${Config.cmsUrl}/random-endpoint-2`
  header: `Bearer ${Config.twitterApiKey}`
});

Enter fullscreen mode Exit fullscreen mode

If I ever need to change the environment variable that the Twitter API key was stored in, I don't have to change a zillion files, I just change it here in config.ts!

If I need to add documentation and group items, I can easily add it here.

export const Config = {

    general: {

      /** The URL for our Craft environment */
      cmsUrl: process.env.NEXT_PUBLIC_CRAFT_CMS_URL,

      jwtSecret: process.env.ZEROCHASS_SECRET,

      /** The stage we're on, should be QA/Dev/Prod */
      nodeEnv: process.env.NODE_ENV,
    },

    database: {
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      name: process.env.DB_NAME,
    },

    aws: {
      region: process.env.AWS_REGION,
      bucket: process.env.AWS_BUCKET,
    },

    twitter: {

      /** API v1 URL for Twitter */
      apiUrl: process.env.TWITTER_API_URL,

      /** API key for our Twitter app */
      apiKey: process.env.TWITTER_API_KEY,
    },
}
Enter fullscreen mode Exit fullscreen mode

And anyone who imports this file will get all that context, including the code hints on hover!

Hopefully this short post has given you some insight on how you might rethink your environment variable usage. You can even throw in some value-validation here, but I won't cover that here.

Let me know your thoughts!

Discussion (1)

Collapse
ekqt profile image
Hector Sosa

Great post, Chris! I really liked the setup to change variables as the application scales. A thing I didn't notice if you are using or not is the dotenv (dependency to load variables) or what other alternative are you using here. Another really nice concept to explore would be how to share your .env file since this file isn't uploaded anywhere. Perhaps an idea for a future post! Thanks for sharing this!