Context
My previous project was very huge and complex, we’re working on automotive industry and our mission - make users happy with minimal involving. The user should fullfil financial info and upload driver license - and then he can purchase any car, matched by his financial capabilities. As I mentioned before - this is huge project with a lot of involving teams. On this project my role - lead and drive core changes of automation framework.
While I was on this project automation team facing with a problem, which is that we have a lot of environment variables, which should be parsed by rules, e.g. jobsCount
, browser
etc. . We need to manage them in a way, which is easy to use and easy to change.
Let’s see how it was before the library usage.
Before
We use dotenv
to parse and load environment variables to process.env
object.
How it was before refactoring.
Implementation
We define all process.env
variables and creates utility with name env
and writes function to load and register
How it was:
Project Structure
Directorysrc
Directoryutils
- index.ts - exports functions from “env” module
Directoryenv
- register.ts - this file is responsible for loading and parse env variables
- index.ts - exported variables are used in “register.ts”
- package.json - dependencies
Let’s deep dive into index.ts
file:
But this variables are predefined and if engineer set them with wrong values - we get an unexpected error or even behavior.
Example:
The problem here with parsing is that we have a lot of variables with various requirements, e.g. numeric value, should be Finite and valid number, string always should be a subset of enum, etc.
The next step is creating utility function to transform by predefined rules.
Example:
Usage in register file
Pros
- we are sure that exact
process.env
variable has been parsed correctly.
Cons:
- for each groups we shall creates new guard, e.g.
numberGuard
,projectSpecificGuard1
,projectSpecificGuard2
, etc. - we need to create tests for each guard.
- supports only project needs guards.
- we are not handle objects, arrays, since
env
variables are described with primitives, in mostly cases is OK, but we are not 100% sure about it :)
After
We made a decision to write own dotenv guards and make it open source.
We was focusing on routine transformations. For numbers it is: undefined
, finite
and safe
getting started:
npm install dotenv-guards
using in our project:
Why dotenv-guards useful?
Well, our API provides fallback value in case of errors, it makes more flexible.
Also, From 2 version - I’ll provides define
and revoke
functions, so if you feel like primitives are not enough - you may define own guard.
Example:
The reason why define
function is exists - is making sure that first argument is env-like(string | undefined
).
It also written on Typescript and all inputs/outputs are strongly typed.
First argument should be string|undefined
. This is essential, since environment-like variables are strings.
You can also define generic function.
Example:
Conclusion
As for me - the best way to implementing env
module is creating an object and JSON schema definitions. Since JSON schema has standards and more flexible.
For example, I’ll take [class-validator](https://github.com/typestack/class-validator)
and [class-transformer](https://github.com/typestack/class-transformer)
packages. And I’ll also pickup dotenv-guards
to transform properties safety.
It will look like:
At this article you see how to use dotenv-guards
library and how it solves our issues in project with dotenv usage and parsing.
I hope you enjoy this article, share it with your friends and colleagues.