API Design in Node.js, v4

Creating Environment Configurations

Scott Moss

Scott Moss

Superfilter AI
API Design in Node.js, v4

Check out a free preview of the full API Design in Node.js, v4 course

The "Creating Environment Configurations" Lesson is part of the full, API Design in Node.js, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Scott creates configuration files for each environment and uses the lodash.merge module to merge custom environment variables into a single configuration object. This architecture allows a default configuration to be created, and for each environment, a customization for a specific environment can override the default.


Transcript from the "Creating Environment Configurations" Lesson

>> Okay, so what we're gonna do is something I was really considering teaching. Cuz I'm like, it's not really necessary, but I think it's such a great foundation to understand this and to be able to do this. It's creating a configuration for your API that's flexible. And it allows you to change things on the fly with minimum effort.

So we can create a configuration that's based off an environment, and it changes. So when we go deploy to different environments, we don't have to change much, and our API becomes environment-driven. So it can adapt and change based off some variables, versus us like, wait, I gotta make a PR to change this to work for staging.

It's like, we don't have to, just change out this environment variable and then we're good. So that's what I wanna do. So please follow along, I have the code right here. This one actually won't be too much to follow, as this is kinda short, but it's actually very powerful.

So let's just hop right into this one. So what we're gonna do is we're gonna create what I call a global config. So if you go into source, I'm gonna create a new folder here, I'm gonna call it config. Inside a config, I'm just gonna make an index.ts.

And then for every single environment that I think I'm gonna run my code in, I'm gonna make another file for it. So I'm gonna make local.ts, I'm gonna make a prod.ts, and maybe testing.ts. I could probably even do staging if I wanted to. So these are just the different modes that I want my app to run in.

They might have different options, like for instance, maybe locally, I wanna make sure analytics are turned off, so I'm not tracking myself. But in production, I want analytics turned on, okay? How would you do that in your app right now? Knowing our Express app, if we had a middleware, let's go to our routes.

Let's say I had a middleware here that did something like this, it said track. Inside that middleware it reported to segment.io or something to track analytics. How would I turn this off from tracking if I'm running locally, versus in production, without actually deleting the code? Right, okay, that's what we're trying to figure out, that's what we're about to create.

We wanna dynamically be able to do that without having to change our code. And this is actually a common one, you really don't wanna be tracking yourself locally in a production. So inside of the index.ts, what we're gonna do is a few things first. By default, NODE_ENV is not set by default, unless you just already set it yourself.

So we wanna do that, but we also need to install this package right here called lodash.merge. If you never used lodash before, it's pretty cool. It's a package that has a bunch of utility functions on it that people don't really need anymore, because it's built into JavaScript. But before it was built into JavaScript, this stuff was really cool.

And this one actually isn't built into JavaScript, merge. So we'll npm install loadash.merge, make sure you save that. Okay, and then what we wanna do is on this next line is, We want to go ahead and set up a default for our NODE_ENV. So what I'm doing is I'm saying, hey, process NODE_ENV equals whatever process NODE_ENV was, if it was anything.

Or it's gonna default to development. So I'm just trying to make sure I don't overwrite it if it already existed, but if it didn't, please set it to development, so I'm gonna do that. Okay. And then what I like to do, I actually like to treat environments and stages separately.

And it kinda goes back to what I said earlier, where it's like maybe you want to run an environment version of your app locally on your computer. So in that example, the stage would be local, the environment would be production. So I actually like to create something different called stage.

So I'm gonna say something called stage here. And well, how do I do that here? Yeah, so stage is gonna be whatever process.env.STAGE is, if you set one. If you didn't set it, then it's just gonna be local, okay? And then now we're gonna create a dynamic config based off the environment.

So now it's gonna say, let envConfig, pretty much, be nothing. And then I'm just gonna say if stage equals, let's say, production. So if we literally are in production environment, this is running on our hosting provider. Then what I wanna do is I wanna require, I wanna say envConfig, if I can spell config, Equals, I wanna use require here, it's gonna require the, I need to get the production config.

And because we're using require and the production file is probably gonna use export, we actually have to do .default here. And this is for interop between ES6 modules and non-ES6 modules, we have to do a .default. Okay, else if, stage equals, I don't know, staging, I guess. Or I'll say testing, we don't have staging, let's say testing, then envConfig = require, testing, so our default.

else envConfig = the ones from require, the local one, it's gonna default to local. And then we're just gonna export default merge our default config on top of, or underneath, our envConfig. So our default config is gonna have all the different variables that we need in our app.

For instance, I know that stage is gonna be whatever stage is. I know that environment, env, is always gonna be whatever process.env.NODE_ENV is. And then I can do things like well, what's the port? Well, by default we'll say the port is 3001. I can also do my secrets here, so I might make something called secrets, that's an object.

And I could say secrets.jwt is always gonna be process.env., JWT_SECRET, all right? I can do that, I can say dbUrl is always gonna be database URL. All right, I can do that, that's the default. But because it's being merged underneath this env config, I can override it depending on what environment I'm in, right?

So right now I'm saying the default port is 3001. And that's great, but I would never do that in production. You can't set a port when you go to production, the port is given to you. So this would break in production. So what I would do is like for prod, yeah, I'm gonna say export, default an object.

For prod, the port is actually, process.env.PORT, because you have to do that for production, it won't work. So when the environment variable is in production, and this gets imported into here and merged on top of it, it will overwrite this port. So port will be processed, that env.PORT now, and not 3001.

So this is how you can dynamically change your app. And then all you have to do is make sure that you use this configuration object everywhere in your app that needs these values. And you're guaranteed to have a dynamic app. So let me show you what I'm talking about.

So I'm gonna say prod has a env.PORT like that, I'm gonna say local is 3001. Actually, I'm just gonna change prod to like 5000 so you can actually see it, so I'm gonna say 5000. So let's do that, let's say 5000. So prod is 5000, local is 3001, okay?

I'm gonna go to our index. I'm gonna import that config, import config from config. I'm gonna get rid of 3001, then I will say config.port. So that's, Okay, so we got config.port, so now I'm gonna start this. We default to development environments, when I start it, it should be on 3001, because that's what local defaults to, right?

But if I start this with NODE_ENV set to production, so if I say NODE_ENV=production. Actually, I think it's still gonna be messed up, because I'm doing stage, right? Let's see. Yeah, I gotta change the stage. So if I say this is stage, then what I will say is, change this to, STAGE = production, I can run that.

And then yeah, it tried to run it on 5000, we already have something on 5000, so I got the error. But it didn't try to run it on 3001, it tried to run on 5000. It switched the config to the production one, which is 5000. So if I change that, I don't know what's on 5000 right now, I'm scared to look at it.

[LAUGH] But let's run it on 5002, and you can see it runs here.
>> Probably your GitHub bot?
>> Yes, probably some bot I deployed like a month ago, and it's like running rampant on my memory right now, I don't know. But yeah, so you can dynamically change the config.

And it's very powerful when you start moving this app around in many different environments. And all we had to do was just change an environment variable. We didn't change any code, we just changed environment variable. We talked about the config. And if you look at the example that I have here on the site, I have a little more examples of things you can do.

For instance, doing things like logging. Sometimes you wanna see different levels of logging, depending on what environment you're in. You wanna see more verbose in development mode. But maybe you don't want as verbose in production mode clogging up your logs when you're trying to find a critical error.

You wanna see something a little more discrete. You can change that one environment variable, or maybe for that one time, you do want something a little more verbose. You can just go into the environment variable, change the config, and then now logging is at different level. So it's extremely powerful, I highly recommend making sure that your API is driven by some type of configuration that's bound to environment variables, so it remains dynamic.

Because I promise you, just like earlier, where I was going through in the end that, I was trying to get that API call to work and it kept failing, and it kept failing, and it kept failing, okay? Imagine that, but every time it fails, you gotta make another pull request for it to deploy.

And then wait for it to deploy, just to find out it doesn't work, and then doing it again, right? So if you can put as much as you can to be dynamic, all you have to do is just change an environment variable. And it's not gonna solve all your problems, but it can solve a lot of them.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now