Skip to content

Commit 852a973

Browse files
committed
🚀 3.1.0: Add fallback option
1 parent f4b355f commit 852a973

File tree

7 files changed

+101
-17
lines changed

7 files changed

+101
-17
lines changed

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ let nodeEnv = envSafe->EnvSafe.get(
5050
]),
5151
~devFallback=#development,
5252
)
53-
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~devFallback=3000)
53+
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~fallback=3000)
5454
let apiUrl = envSafe->EnvSafe.get("API_URL", S.string->S.String.url, ~devFallback="https://example.com/graphql")
5555
let auth0ClientId = envSafe->EnvSafe.get("AUTH0_CLIENT_ID", S.string)
5656
let auth0Domain = envSafe->EnvSafe.get("AUTH0_DOMAIN", S.string)
@@ -73,10 +73,10 @@ Creates `envSafe` to start working with environment variables. By default it use
7373

7474
### **`EnvSafe.get`**
7575

76-
`(EnvSafe.t, string, S.t<'value>, ~allowEmpty: bool=?, ~devFallback: 'value=?, ~input: option<string>=?) => 'value`
76+
`(EnvSafe.t, string, S.t<'value>, ~allowEmpty: bool=?, ~fallback: 'value=?, ~devFallback: 'value=?, ~input: option<string>=?) => 'value`
7777

7878
```rescript
79-
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~devFallback=3000)
79+
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~fallback=3000)
8080
```
8181

8282
Gets an environment variable from `envSafe` applying coercion and parsing logic of `schema`.
@@ -87,7 +87,8 @@ Gets an environment variable from `envSafe` applying coercion and parsing logic
8787
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
8888
| `name` | `string` | Name of the environment variable |
8989
| `schema` | `S.t<'value>` | A schema created with **[rescript-schema](https://github.com/DZakh/rescript-schema)**. It's used for coercion and parsing. For bool schemas coerces `"0", "1", "true", "false", "t", "f"` to boolean values. For int and float schemas coerces string to number. For other non-string schemas the value is coerced using `JSON.parse` before being validated. |
90-
| `devFallback` | `'value=?` | A fallback value to use only when `NODE_ENV` is not `production`. This is handy for env vars that are required for production environments, but optional for development and testing. If you need to set fallback value for all environments, you can use `S.Option.getOr` on schema. |
90+
| `fallback` | `'value=?` | A fallback value when the environment variable is missing. |
91+
| `devFallback` | `'value=?` | A fallback value to use only when `NODE_ENV` is not `production`. This is handy for env vars that are required for production environments, but optional for development and testing. |
9192
| `input` | `string=?` | As some environments don't allow you to dynamically read env vars, we can manually put it in as well. Example: `input=%raw("process.env.NEXT_PUBLIC_API_URL")`. |
9293
| `allowEmpty` | `bool=false` | Default behavior is `false` which treats empty strings as the value is missing. if explicit empty strings are OK, pass in `true`. |
9394

__tests__/EnvSafe_test.res

+67
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,53 @@ test("Uses devFallback value when env is missing", t => {
149149
}, ())
150150
})
151151

152+
test("Uses fallback value when env is missing", t => {
153+
let envSafe = EnvSafe.make(
154+
~env=Obj.magic({
155+
"STRING_ENV": "abc",
156+
}),
157+
)
158+
159+
t->Assert.is(
160+
envSafe->EnvSafe.get(
161+
"MISSING_ENV",
162+
S.literal("invalid")->S.variant(_ => #polymorphicToTestFunctionType2),
163+
~fallback=#polymorphicToTestFunctionType,
164+
),
165+
#polymorphicToTestFunctionType,
166+
(),
167+
)
168+
t->Assert.notThrows(() => {
169+
envSafe->EnvSafe.close
170+
}, ())
171+
})
172+
173+
type fallbackTestVariant = ReadResult | FallbackResult | DevFallbackResult
174+
test(
175+
"Uses devFallback value over fallback when env is missing and NODE_ENV is not set to production",
176+
t => {
177+
let envSafe = EnvSafe.make(
178+
~env=Obj.magic({
179+
"STRING_ENV": "abc",
180+
}),
181+
)
182+
183+
t->Assert.is(
184+
envSafe->EnvSafe.get(
185+
"MISSING_ENV",
186+
S.literal(ReadResult),
187+
~fallback=FallbackResult,
188+
~devFallback=DevFallbackResult,
189+
),
190+
DevFallbackResult,
191+
(),
192+
)
193+
t->Assert.notThrows(() => {
194+
envSafe->EnvSafe.close
195+
}, ())
196+
},
197+
)
198+
152199
test("Doesn't use devFallback value when NODE_ENV is production", t => {
153200
let envSafe = EnvSafe.make(
154201
~env=Obj.magic({
@@ -310,3 +357,23 @@ test("Applies preprocessor logic for union schemas separately", t => {
310357
envSafe->EnvSafe.close
311358
}, ())
312359
})
360+
361+
test("Fails to access EnvSafe after close", t => {
362+
let envSafe = EnvSafe.make(
363+
~env=Obj.magic({
364+
"STRING_ENV": "abc",
365+
}),
366+
)
367+
368+
t->Assert.notThrows(() => {
369+
envSafe->EnvSafe.close
370+
}, ())
371+
372+
t->Assert.throws(
373+
() => {envSafe->EnvSafe.get("STRING_ENV", S.string)},
374+
~expectations={
375+
message: "[rescript-envsafe] EnvSafe is closed. Make a new one to get access to environment variables.",
376+
},
377+
(),
378+
)
379+
})

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rescript-envsafe",
3-
"version": "3.0.0",
3+
"version": "3.1.0",
44
"description": "🔒 Makes sure you don't accidentally deploy apps with missing or invalid environment variables",
55
"homepage": "https://github.com/DZakh/rescript-envsafe#readme",
66
"keywords": [

src/EnvSafe.bs.js

+16-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EnvSafe.res

+9-5
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ let get = (
197197
name,
198198
schema,
199199
~allowEmpty=false,
200+
~fallback as maybeFallback=?,
200201
~devFallback as maybeDevFallback=?,
201202
~input as maybeInlinedInput=?,
202203
) => {
@@ -208,13 +209,16 @@ let get = (
208209
| None => envSafe.env->Stdlib.Dict.get(name)
209210
}
210211
let parseResult = input->S.parseAnyWith(prepareSchema(~schema, ~allowEmpty))
211-
switch (parseResult, maybeDevFallback) {
212-
| (Ok(v), _) => v
213-
| (Error({code: InvalidLiteral({received})}), Some(devFallback))
214-
| (Error({code: InvalidType({received})}), Some(devFallback))
212+
switch (parseResult, maybeDevFallback, maybeFallback) {
213+
| (Ok(v), _, _) => v
214+
| (Error({code: InvalidLiteral({received})}), Some(devFallback), _)
215+
| (Error({code: InvalidType({received})}), Some(devFallback), _)
215216
if received === %raw(`undefined`) &&
216217
envSafe.env->Stdlib.Dict.get("NODE_ENV") !== Some("production") => devFallback
217-
| (Error(error), _) => {
218+
| (Error({code: InvalidLiteral({received})}), _, Some(fallback))
219+
| (Error({code: InvalidType({received})}), _, Some(fallback))
220+
if received === %raw(`undefined`) => fallback
221+
| (Error(error), _, _) => {
218222
envSafe->mixinIssue({name, error, input})
219223
%raw(`undefined`)
220224
}

src/EnvSafe.resi

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ let get: (
1212
string,
1313
S.t<'value>,
1414
~allowEmpty: bool=?,
15+
~fallback: 'value=?,
1516
~devFallback: 'value=?,
1617
~input: option<string>=?,
1718
) => 'value

0 commit comments

Comments
 (0)