1 | <p align="center">
|
2 | <img
|
3 | src="https://raw.githubusercontent.com/af/envalid/main/.github/logo.svg"
|
4 | width="160"
|
5 | alt="Envalid text logo with drop shadow"
|
6 | />
|
7 | </p>
|
8 |
|
9 | <p align="center">
|
10 | <strong>
|
11 | Envalid is a small library for validating and accessing<br />
|
12 | environment variables in Node.js programs
|
13 | </strong>
|
14 | </p>
|
15 |
|
16 | <p align="center">
|
17 | <a href="https://github.com/af/envalid/actions/workflows/ci.yml">
|
18 | <img src="https://github.com/af/envalid/workflows/continuous-integration/badge.svg" alt="Current GitHub Build Status Badge" />
|
19 | </a>
|
20 | </p>
|
21 |
|
22 | Envalid is a small library for validating and accessing environment variables in
|
23 | Node.js programs, aiming to:
|
24 |
|
25 | - Ensure that your program only runs when all of its environment dependencies are met
|
26 | - Give you executable documentation about the environment your program expects to run in
|
27 | - Give you an immutable API for your environment variables, so they don't change
|
28 | from under you while the program is running
|
29 |
|
30 | ## Why Envalid?
|
31 |
|
32 | - Type-safe: written completely in TypeScript, with great support for inference
|
33 | - Light: no dependencies besides [tslib](https://github.com/Microsoft/tslib)
|
34 | - Modular: customize behavior with custom validators, middleware, and reporters
|
35 |
|
36 | ## API
|
37 |
|
38 | ### `envalid.cleanEnv(environment, validators, options)`
|
39 |
|
40 | `cleanEnv()` returns a sanitized, immutable environment object, and accepts three
|
41 | positional arguments:
|
42 |
|
43 | - `environment` - An object containing your env vars (eg. `process.env`)
|
44 | - `validators` - An object that specifies the format of required vars.
|
45 | - `options` - An (optional) object, which supports the following key:
|
46 | - `reporter` - Pass in a function to override the default error handling and
|
47 | console output. See `src/reporter.ts` for the default implementation.
|
48 |
|
49 | By default, `cleanEnv()` will log an error message and exit (in Node) or throw (in browser) if any required
|
50 | env vars are missing or invalid. You can override this behavior by writing your own reporter.
|
51 |
|
52 | ```js
|
53 | import { cleanEnv, str, email, json } from 'envalid'
|
54 |
|
55 | const env = cleanEnv(process.env, {
|
56 | API_KEY: str(),
|
57 | ADMIN_EMAIL: email({ default: 'admin@example.com' }),
|
58 | EMAIL_CONFIG_JSON: json({ desc: 'Additional email parameters' }),
|
59 | NODE_ENV: str({ choices: ['development', 'test', 'production', 'staging'] }),
|
60 | })
|
61 |
|
62 | // Read an environment variable, which is validated and cleaned during
|
63 | // and/or filtering that you specified with cleanEnv().
|
64 | env.ADMIN_EMAIL // -> 'admin@example.com'
|
65 |
|
66 | // Envalid checks for NODE_ENV automatically, and provides the following
|
67 | // shortcut (boolean) properties for checking its value:
|
68 | env.isProduction // true if NODE_ENV === 'production'
|
69 | env.isTest // true if NODE_ENV === 'test'
|
70 | env.isDev // true if NODE_ENV === 'development'
|
71 | ```
|
72 |
|
73 | For an example you can play with, clone this repo and see the `example/` directory.
|
74 |
|
75 | ```
|
76 | git clone https://github.com/af/envalid
|
77 | cd envalid
|
78 | yarn prepare
|
79 | node example/server.js
|
80 | ```
|
81 |
|
82 | ## Validator types
|
83 |
|
84 | Node's `process.env` only stores strings, but sometimes you want to retrieve other types
|
85 | (booleans, numbers), or validate that an env var is in a specific format (JSON,
|
86 | URL, email address). To these ends, the following validation functions are available:
|
87 |
|
88 | - `str()` - Passes string values through, will ensure a value is present unless a
|
89 | `default` value is given. Note that an empty string is considered a valid value -
|
90 | if this is undesirable you can easily create your own validator (see below)
|
91 | - `bool()` - Parses env var strings `"1", "0", "true", "false", "t", "f"` into booleans
|
92 | - `num()` - Parses an env var (eg. `"42", "0.23", "1e5"`) into a Number
|
93 | - `email()` - Ensures an env var is an email address
|
94 | - `host()` - Ensures an env var is either a domain name or an ip address (v4 or v6)
|
95 | - `port()` - Ensures an env var is a TCP port (1-65535)
|
96 | - `url()` - Ensures an env var is a URL with a protocol and hostname
|
97 | - `json()` - Parses an env var with `JSON.parse`
|
98 |
|
99 | Each validation function accepts an (optional) object with the following attributes:
|
100 |
|
101 | - `choices` - An Array that lists the admissible parsed values for the env var.
|
102 | - `default` - A fallback value, which will be present in the output if the env var wasn't specified.
|
103 | Providing a default effectively makes the env var optional. Note that `default`
|
104 | values are not passed through validation logic, they are default _output_ values.
|
105 | - `devDefault` - A fallback value to use _only_ when `NODE_ENV` is explicitly set and _not_ `'production'`.
|
106 | This is handy for env vars that are required for production environments, but optional
|
107 | for development and testing.
|
108 | - `desc` - A string that describes the env var.
|
109 | - `example` - An example value for the env var.
|
110 | - `docs` - A URL that leads to more detailed documentation about the env var.
|
111 |
|
112 | ## Custom validators
|
113 |
|
114 | ### Basic usage
|
115 |
|
116 | You can easily create your own validator functions with `envalid.makeValidator()`. It takes
|
117 | a function as its only parameter, and should either return a cleaned value, or throw if the
|
118 | input is unacceptable:
|
119 |
|
120 | ```js
|
121 | import { makeValidator, cleanEnv } from 'envalid'
|
122 | const twochars = makeValidator((x) => {
|
123 | if (/^[A-Za-z]{2}$/.test(x)) return x.toUpperCase()
|
124 | else throw new Error('Expected two letters')
|
125 | })
|
126 |
|
127 | const env = cleanEnv(process.env, {
|
128 | INITIALS: twochars(),
|
129 | })
|
130 | ```
|
131 |
|
132 | ### TypeScript users
|
133 |
|
134 | You can use either one of `makeValidator`, `makeExactValidator` and `makeStructuredValidator`
|
135 | depending on your use case.
|
136 |
|
137 | #### `makeValidator<BaseT>`
|
138 |
|
139 | This validator has the output narrowed down to a subtype of `BaseT` (e.g. `str`).
|
140 | Example of a custom integer validator:
|
141 |
|
142 | ```ts
|
143 | const int = makeValidator<number>((input: string) => {
|
144 | const coerced = parseInt(input, 10)
|
145 | if (Number.isNaN(coerced)) throw new EnvError(`Invalid integer input: "${input}"`)
|
146 | return coerced
|
147 | })
|
148 | const MAX_RETRIES = int({ choices: [1, 2, 3, 4] })
|
149 | // Narrows down output type to '1 | 2 | 3 | 4' which is a subtype of 'number'
|
150 | ```
|
151 |
|
152 | #### `makeExactValidator<T>`
|
153 |
|
154 | This validator has the output widened to `T` (e.g. `bool`). To understand the difference
|
155 | with `makeValidator`, let's use it in the same scenario:
|
156 |
|
157 | ```ts
|
158 | const int = makeExactValidator<number>((input: string) => {
|
159 | const coerced = parseInt(input, 10)
|
160 | if (Number.isNaN(coerced)) throw new EnvError(`Invalid integer input: "${input}"`)
|
161 | return coerced
|
162 | })
|
163 | const MAX_RETRIES = int({ choices: [1, 2, 3, 4] })
|
164 | // Output type is 'number'
|
165 | ```
|
166 |
|
167 | As you can see in this instance, _the output type is exactly `number`, the parameter type of
|
168 | `makeExactValidator`_. Also note that here, `int` is not parametrizable.
|
169 |
|
170 | ## Error Reporting
|
171 |
|
172 | By default, if any required environment variables are missing or have invalid
|
173 | values, Envalid will log a message and call `process.exit(1)`. You can override
|
174 | this behavior by passing in your own function as `options.reporter`. For example:
|
175 |
|
176 | ```js
|
177 | const env = cleanEnv(process.env, myValidators, {
|
178 | reporter: ({ errors, env }) => {
|
179 | emailSiteAdmins('Invalid env vars: ' + Object.keys(errors))
|
180 | },
|
181 | })
|
182 | ```
|
183 |
|
184 | Additionally, Envalid exposes `EnvError` and `EnvMissingError`, which can be checked in case specific error handling is desired:
|
185 |
|
186 | ```js
|
187 | const env = cleanEnv(process.env, myValidators, {
|
188 | reporter: ({ errors, env }) => {
|
189 | for (const [envVar, err] of Object.entries(errors)) {
|
190 | if (err instanceof envalid.EnvError) {
|
191 | ...
|
192 | } else if (err instanceof envalid.EnvMissingError) {
|
193 | ...
|
194 | } else {
|
195 | ...
|
196 | }
|
197 | }
|
198 | }
|
199 | })
|
200 | ```
|
201 |
|
202 | ## Custom Middleware (advanced)
|
203 |
|
204 | In addition to `cleanEnv()`, as of v7 there is a new `customCleanEnv()` function,
|
205 | which allows you to completely replace the processing that Envalid applies after applying
|
206 | validations. You can use this custom escape hatch to transform the output however you wish.
|
207 |
|
208 | ### `envalid.customCleanEnv(environment, validators, applyMiddleware, options)`
|
209 |
|
210 | `customCleanEnv()` uses the same API as `cleanEnv()`, but with an additional `applyMiddleware`
|
211 | argument required in the third position:
|
212 |
|
213 | - `applyMiddleware` - A function that can modify the env object after it's
|
214 | validated and cleaned. Envalid ships (and exports) its own default
|
215 | middleware (see src/middleware.ts), which you can mix and match with your own
|
216 | custom logic to get the behavior you desire.
|
217 |
|
218 | ## Utils
|
219 |
|
220 | ### testOnly
|
221 |
|
222 | The `testOnly` helper function is available for setting a default value for an env var only when `NODE_ENV=test`. It is recommended to use this function along with `devDefault`. For example:
|
223 |
|
224 | ```js
|
225 | const env = cleanEnv(process.env, {
|
226 | SOME_VAR: envalid.str({ devDefault: testOnly('myTestValue') }),
|
227 | })
|
228 | ```
|
229 |
|
230 | For more context see [this issue](https://github.com/af/envalid/issues/32).
|
231 |
|
232 | ## FAQ
|
233 |
|
234 | ### Can I call `structuredClone()` on Envalid's validated output?
|
235 |
|
236 | Since by default Envalid's output is wrapped in a Proxy, structuredClone [will not work](https://bugzilla.mozilla.org/show_bug.cgi?id=1269327#c1) on it. See [#177](https://github.com/af/envalid/issues/177).
|
237 |
|
238 | ## Related projects
|
239 |
|
240 | - [dotenv](https://www.npmjs.com/package/dotenv) is a very handy tool for loading env vars from
|
241 | `.env` files. It was previously used as a dependency of Envalid. To use them together, simply
|
242 | call `require('dotenv').config()` before you pass `process.env` to your `envalid.cleanEnv()`.
|
243 |
|
244 | - [react-native-config](https://www.npmjs.com/package/react-native-config) can be useful for React Native projects for reading env vars from a `.env` file
|
245 |
|
246 | - [fastify-envalid](https://github.com/alemagio/fastify-envalid) is a wrapper for using Envalid within [Fastify](https://www.fastify.io/)
|
247 |
|
248 | - [nestjs-envalid](https://github.com/simenandre/nestjs-envalid) is a wrapper for using Envalid with [NestJS](https://nestjs.com/)
|
249 |
|
250 | - [nuxt-envalid](https://github.com/manuelhenke/nuxt-envalid) is a wrapper for using Envalid with [NuxtJS](https://nuxtjs.org/)
|
251 |
|
252 | ## Motivation
|
253 |
|
254 | http://www.12factor.net/config
|