Giter VIP home page Giter VIP logo

env-var's Introduction

env-var

NPM version TypeScript License Coverage Status npm downloads Known Vulnerabilities

Verification, sanitization, and type coercion for environment variables in Node.js and web applications. Supports TypeScript!

  • πŸ‹ Lightweight. Zero dependencies and just ~4.7kB when minified!
  • 🧹 Clean and simple code, as shown here.
  • 🚫 Fails fast if your environment is misconfigured.
  • πŸ‘©β€πŸ’» Friendly error messages and example values for better debugging experience.
  • πŸŽ‰ TypeScript support provides compile time safety and better developer experience.
  • πŸ“¦ Support for frontend projects, e.g in React, React Native, Angular, etc.

Contents

Install

npm

npm install env-var

yarn

yarn add env-var

Getting started

You can use env-var in both JavaScript and TypeScript!

Node.js Javascript example

const env = require('env-var');

// Or using module import syntax:
// import env from 'env-var'

const PASSWORD = env.get('DB_PASSWORD')
  // Throws an error if the DB_PASSWORD variable is not set (optional)
  .required()
  // Decode DB_PASSWORD from base64 to a utf8 string (optional)
  .convertFromBase64()
  // Call asString (or other APIs) to get the variable value (required)
  .asString();

// Read in a port (checks that PORT is in the range 0 to 65535)
// Alternatively, use a default value of 5432 if PORT is not defined
const PORT = env.get('PORT').default('5432').asPortNumber()

Node.js TypeScript example

import * as env from 'env-var';

// Read a PORT environment variable and ensure it's a positive integer.
// An EnvVarError will be thrown if the variable is not set, or if it
// is not a positive integer.
const PORT: number = env.get('PORT').required().asIntPositive();

WebApp Example

When using environment variables in a web application, usually your tooling such as vite imposes special conventions and doesn't expose process.env. Use from function to workaround this, and create an env object like so:

import { from } from 'env-var'

const env = from({
  BASE_URL: import.meta.env.BASE_URL,
  VITE_CUSTOM_VARIABLE: import.meta.env.CUSTOM_VARIABLE
})

For more examples, refer to the /example directory and EXAMPLE.md. A summary of the examples available in /example is written in the 'Other examples' section of EXAMPLE.md.

API

The examples above only cover a very small set of env-var API calls. There are many others such as asFloatPositive(), asJson() and asRegExp(). For a full list of env-var API calls, check out API.md.

You can also create your own custom accessor; refer to the 'extraAccessors' section of API.md.

Logging

Logging is disabled by default in env-var to prevent accidental logging of secrets.

To enable logging, you need to create an env-var instance using the from() function that the API provides and pass in a logger.

  • A built-in logger is available, but a custom logger is also supported.
  • Always exercise caution when logging environment variables!

Using the Built-in Logger

The built-in logger will print logs only when NODE_ENV is not set to either prod or production.

const { from, logger } =  require('env-var')
const env = from(process.env, {}, logger)

const API_KEY = env.get('API_KEY').required().asString()

This is an example output from the built-in logger generated by running example/logging.js:

logging example output

Using a Custom Logger

If you need to filter env-var logs based on log levels (e.g. trace logging only) or have your own preferred logger, you can use a custom logging solution such as pino easily.

See the 'Custom logging' section of EXAMPLE.md for more information.

Optional integration with dotenv

You can optionally use dotenv with env-var.

There is no coupling between dotenv and env-var, but you can easily use them both together. This loose coupling reduces package bloat and allows you to start or stop using one without being forced to do the same for the other.

See the 'dotenv' section of EXAMPLE.md for more information.

Contributing

Contributions are welcomed and discussed in CONTRIBUTING.md. If you would like to discuss an idea, open an issue or a PR with an initial implementation.

Contributors

  • @aautio
  • @avocadomaster
  • @caccialdo
  • @ChibiBlasphem
  • @DigiPie
  • @dror-weiss
  • @evanshortiss
  • @gabrieloczkowski
  • @hhravn
  • @ineentho
  • @itavy
  • @jerome-fox
  • @joh-klein
  • @Lioness100
  • @MikeyBurkman
  • @pepakriz
  • @rmblstrp
  • @shawnmclean
  • @todofixthis
  • @xuo

env-var's People

Contributors

avocadomaster avatar bigen1925 avatar caccialdo avatar coox avatar digipie avatar dror-weiss avatar evanshortiss avatar gabrieloczkowski avatar greenkeeper[bot] avatar hckhanh avatar hhravn avatar hixus avatar ineentho avatar itavy avatar jerome-fox avatar joh-klein avatar kodaka avatar lioness100 avatar mikeyburkman avatar pepakriz avatar rmblstrp avatar shawnmclean avatar ted-dino avatar todofixthis avatar xuo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

env-var's Issues

An in-range update of coveralls is breaking the build 🚨

The devDependency coveralls was updated from 3.0.5 to 3.0.6.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

coveralls is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).
  • βœ… coverage/coveralls: First build on greenkeeper/coveralls-3.0.6 at 100.0% (Details).

Commits

The new version differs by 3 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Add a "example()" function

Is your feature request related to a problem? Please describe.
The idea comes from issue #95. Perhaps it would be helpful to provide an example value, without having to provide a default.

Describe the solution you'd like
A .example('sample-value') function that is a noop, but assists code readability. The example value could also be used by error messages.

asEnum should accept readonly T[]

I would like to be able to write the code below in typescript. However, asEnum does not accept a readonly T[] value

const environmentTypes = ['development', 'staging', 'production'] as const
type EnvironmentType = typeof environmentTypes[number]

export const environment = env
  .get('ENVIRONMENT')
  .required()
  .asEnum<EnvironmentType>(environmentTypes)

Types for extra accessors suggest they can't be used with default() or required() (though they can)

Describe the bug πŸ›

In TypeScript, if I attempt to use an accessor defined via the extraAccessors machinery in conjunction with default or required, TypeScript complains; in strict mode at least, it's a compilation error.

It's possible to work around this with as any, but perhaps the type definitions could be fixed so that's not necessary.

When I first noticed this, I concluded that extra accessors couldn't be used with default or required at all and nearly gave up on them; luckily I realised it might just be a typing problem, and that turns out to be true.

(BTW, I mention default and required but I suspect this may also be true for convertFromBase64 and example.)

To Reproduce πŸ“
README.md gives this example of using extraAccessors with TypeScript:

import { from, ExtensionFn, EnvVarError } from 'env-var'

process.env.ADMIN = '[email protected]'

const asEmail: ExtensionFn<string> = (value) => {
  const split = String(value).split('@')
  if (split.length !== 2) {
    throw new Error('must contain exactly one "@"')
  }
  return value
}

const env = from(process.env, {asEmail})

env.get('ADMIN').asEmail()

That works, but either of the following lines:

env.get('ADMIN').default('[email protected]').asEmail()
env.get('ADMIN').required().asEmail()

cause compilation to fail with this error:

Property 'asEmail' does not exist on type 'IPresentVariable'.  TS2339

The workaround is of course to relax the types, e.g.:

(env.get('ADMIN').default('[email protected]') as any).asEmail()
(env.get('ADMIN').required() as any).asEmail()

That all seems to work as expected (by which I mean the expected semantics of default and required seem to be respected), but it's a bit ugly.

Expected behaviour πŸ€·β€β™‚οΈπŸ€·

Just that the above works without a compilation error. Obviously the accessor names are arbitrary and can't be known to the library, so perhaps there just needs to be a more relaxed type in use for extra accessors (and some modification of ExtensionFn accordingly?) β€” but I'm waving my hands around here, if I'm honest. πŸ€ͺ

Environment πŸ’»:

  • OS: macOS Mojave 10.14.6
  • Runtime: Chrome 79.0.3945.130
  • Version 6.0.1

Additional context

When @xuo opened issue #83, he mentioned that:

extraAccessors are not chainable with required()

but wasn't the core topic of that issue and wasn't addressed at that time; it certainly looks now like they can be chained, so I wonder if @xuo made the same mistake I almost made, of seeing the type error and assuming that it just doesn't work (even though it seems to).

Install fails on Node 10

The version of nan in the dependencies of leveldown causes a failure during installation, with this error during node-gyp build:

error: β€˜class v8::Object’ has no member named β€˜ForceSet’

nodejs/nan#504 (comment)

is it possible to use nested settings in "from"

Hi,

is it possible to get value from nested json file if I use env.from({...})?

Example:

{
    "API_BASE_URL": {
        "DEVELOP": "https://develop-server.com/api",
        "STAGING": "https://staging-server.com/api",
        "PROD": "https://production-server.com/api"
    },
    ...
}

Currently I have an error: EnvVarError: env-var: "API_BASE_URL.DEVELOP" is a required variable, but it was not set

What I do:

...
const envConfig = require('./config.env.json');
const env = require('env-var').from(envConfig);
...
API_BASE_URL = env.get('API_BASE_URL.DEVELOP').required().asUrlString()
...

Add better type checking

Is your feature request related to a problem? Please describe.

Problem: lack of tests for types can introduce defects.

This is a spillover from the discussion in #117 and defects from #119.

Describe the solution you'd like

Add testing for types.

Found this interesting library today: expect-type

Describe alternatives you've considered

Using:

  • tsd
  • conditional-type-checks (currently used)

Another drastic alternative is to rewrite the project to TypeScript, which will then remove the need to maintain separate types.

Additional context

Can help with a PR.

Add generic type to asEnum

Would be great to have a generic type available for asEnum method.

asEnum: (validValues: string[]) => string;

Should be:

 asEnum: <T extends string>(validValues: T[]) => T;

This would allow to pass arrays with custom types, e.g. string unions.

Duplicate identifier causing broken build with typescript.

Describe the bug πŸ›
EnvVarError is exported twice from the env-var.d.ts file.

$ nest build
node_modules/env-var/env-var.d.ts:263:14 - error TS2300: Duplicate identifier 'EnvVarError'.

263 export class EnvVarError extends Error ***
                 ~~~~~~~~~~~
node_modules/env-var/env-var.d.ts:308:14 - error TS2300: Duplicate identifier 'EnvVarError'.

308 export const EnvVarError: EnvVarError
                 ~~~~~~~~~~~

Found 2 error(s).

info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
The command '/bin/sh -c yarn build' returned a non-zero code: 1

To Reproduce πŸ“
build a typescript project with the latest version.

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
no broken builds would be nice πŸ˜ƒ

Environment (please complete the following information) πŸ’»:

  • OS: Ubuntu
  • Runtime Node.js
  • Version 7.1.0

The number accessors are too permissive

Describe the bug πŸ›
The number accessors are too permissive.
The parseInt and parseFloat functions allow strings beginning with numbers.

To Reproduce πŸ“

parseInt('123hello') // 123
parseFloat('192.168.1.1') // 192.168

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
Sending '123hello' or '192.168.1.1' should throw an exception

Additional context
The solution could be to check if the parsed value is stringify, it should be the same as the input value.
I've a PR ready if you're interested.

Feature request: asRegExp()

Sometimes it it would be useful to be able to parse an environment variable into a regular expression. A common use case for me is a pattern for whitelisted CORS hosts.

My suggested api would be:

.asRegExp(flags = undefined)

where the environment variable should be passed to the first (pattern) parameter of the RegExp constructor and the flags as the second parameter.

Including regular expression support would be nice as there is some error checking for invalid regexps could be included in the library rather than each application.

If you would conciser including this feature, I'd gladly contribute a pull request.

.asIntPositive/Negative both true for 0

To quote from the docs:

asIntPositive()
Performs the same task as asInt(), but also verifies that the number is positive (greater than zero).

asIntNegative()
Performs the same task as asInt(), but also verifies that the number is negative (less than zero).

This is very close to what the Wikipedia entry says about integers with the important addition, that zero is neither:

An integer is positive if it is greater than zero and negative if it is less than zero. Zero is defined as neither negative nor positive.

But when I look in the code, here and here you specifically leave out 0 which makes it both positive and negative.

I actually don't mind that, I would only like it, if it said so in the docs :)

Duplicate identifier 'EnvVarError'.

Describe the bug πŸ›

Got the following errors on the latest version (v7.1.0):

Error: node_modules/env-var/env-var.d.ts(263,14): error TS2300: Duplicate identifier 'EnvVarError'.
Error: node_modules/env-var/env-var.d.ts(308,14): error TS2300: Duplicate identifier 'EnvVarError'.

To Reproduce πŸ“

N/A

Expected behaviour πŸ€·β€β™‚οΈπŸ€·

Not to get TS build errors.

Screenshots πŸ“·

N/A

Environment (please complete the following information) πŸ’»:

N/A

Additional context

N/A

Feature: ability to add custom accessors

As a developer, I would like to define custom accessors, so that I can work with env vars in formats specific to my application's requirements.

For example:

const env = from(process.env, {
  asNodes: (raiseError, value, type = Set) => {
    return new type(
      value.split(',')
        .map(s => s.trim())
        .map(s => s.split(':', 2))
        .map(([ip, port = 80]) => ({ ip, port }))
    )
  }
})

const bootnodes = env.get('BOOTNODES').asNodes()

Proposed acceptance criteria:

  • A developer can call from() and provide an optional object as a second parameter. The object defines additional accessors for any value returned by that instance's get().
  • If the name matches an existing accessor, the existing accessor is replaced by the new one for all subsequent calls to env.get().

TS Error with v6.0.3

node_modules/env-var/env-var.d.ts:258:5 - error TS2314: Generic type 'IPresentVariable<Extensions>' requires 1 type argument(s).

258     IPresentVariable & Record<K, (...args: any[]) => ReturnType<T[K]>>,
        ~~~~~~~~~~~~~~~~

node_modules/env-var/env-var.d.ts:259:5 - error TS2314: Generic type 'IOptionalVariable<Extensions>' requires 1 type argument(s).

259     IOptionalVariable & Record<K, (...args: any[]) => ReturnType<T[K]>|undefined>
        ~~~~~~~~~~~~~~~~~

node_modules/env-var/env-var.d.ts:276:39 - error TS2314: Generic type 'IOptionalVariable<Extensions>' requires 1 type argument(s).

276 export function get(varName: string): IOptionalVariable;
                                          ~~~~~~~~~~~~~~~~~

Environment (please complete the following information) πŸ’»:

  • OS: macOS
  • Runtime node.js
  • Version 12.16.1

An in-range update of @types/node is breaking the build 🚨

The devDependency @types/node was updated from 12.12.13 to 12.12.14.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/node is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).
  • βœ… coverage/coveralls: First build on greenkeeper/@types/node-12.12.14 at 100.0% (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Typo in repo description 'sanatization'

Describe the bug πŸ›

There's a typo in the repository description:

"Verification, sanatization, and type coercion for environment variables in Node.js"

To Reproduce πŸ“
N/A

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
It should be 'sanitization' instead.

Screenshots πŸ“·
N/A

Environment (please complete the following information) πŸ’»:
N/A

Additional context
Thanks for writing and maintaining this npm module. Love it!

default('') === undefined but should be an empty string

Describe the bug πŸ›
If default is an empty string undefined is returned.

To Reproduce πŸ“

const env = require('env-var')

// Use POOL_SIZE if set, else use an empty value
const POOL_SIZE = env.get('POOL_SIZE').default('').asString()

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
POOL_SIZE === '' should be true, not false

Environment (please complete the following information) πŸ’»:

  • OS: macOS 10.15.2
  • Runtime node.js
  • Version 12.16.1

Deno Support

It would be cool if we could use this library in Deno. πŸ˜„

add asMailString()

Is your feature request related to a problem? Please describe.
is something interesting, there is an email validation when string, I would liken [...]

Describe the solution you'd like
do a regex validation over email, someone can refine it in the future and check IDN and DNS

Additional context

const email = env.get('MAIL_USER').asMailString();

ExtenderType, ExtenderTypeOptional do not satisfy the constraint

Describe the bug πŸ›

I think 34759d2 introduced a bug to TS definitions.

(type parameter) T in type ExtenderType<T>
Type 'T[P]' does not satisfy the constraint '(...args: any) => any'.
  Type 'T[keyof T]' is not assignable to type '(...args: any) => any'.
    Type 'T[string] | T[number] | T[symbol]' is not assignable to type '(...args: any) => any'.
      Type 'T[string]' is not assignable to type '(...args: any) => any'.ts(2344)

To Reproduce πŸ“

Build failure:

https://github.com/ScaleLeap/config/pull/44/checks?check_run_id=602773264

But also just inspecting the env-var.d.ts will display the errors in VS Code.

Expected behaviour πŸ€·β€β™‚οΈπŸ€·

No errors.

Screenshots πŸ“·

Screen-Shot-2020-04-21-16-03-16

Environment (please complete the following information) πŸ’»:

  • OS: macOS
  • Runtime: VS Code, CLI
  • Version:
❯ tsc -v
Version 3.8.3
❯ node -v
v12.16.0

Additional context

N/A

An in-range update of @types/node is breaking the build 🚨

The devDependency @types/node was updated from 12.7.0 to 12.7.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/node is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).
  • βœ… coverage/coveralls: First build on greenkeeper/@types/node-12.7.1 at 100.0% (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Don't default export `from(process.env)` for environments that don't have `process`

Trying to use this lib with vite which uses import.meta.env as the default location for env vars and I get the following error

process is not defined
    at node_modules/env-var/env-var.js (env-var.js:55:23)

and looks like it's happening because the module exports from(process.env) by default and its failing since process doesn't actually exist within the vite environment i am in (sveltekit).

module.exports = from(process.env)

The code:

import { from } from 'env-var';

const env = from(import.meta.env);

export const API_BASE_URL = env.get('VITE_API_BASE_URL').required().asUrlString();

Configure default behavior / Support dotenv-defaults

Is your feature request related to a problem? Please describe.
I'm using dotenv-defaults in my projects. This allows you to have your default values in a file called .env.defaults, which are overriden by the .env file. Now because of that the defaults are actually handled by this package and therefore I have to use the required method of your package a lot of times.

import 'dotenv-defaults/config'

import * as env from 'env-var'

export default {
    cms: {
        // has a default, but "required" is added because the resulting
        // type would be "string | undefined" without it
        app: env.get('VIDEOPLAYER__CMS__APP').required().asString(), 
        baseUrl: env.get('VIDEOPLAYER__CMS__BASE_URL').required().asString(),
        
        // here "required" is valid because it doesn't have a default
        password: env.get('VIDEOPLAYER__CMS__PASSWORD').required().asString(),
        username: env.get('VIDEOPLAYER__CMS__USERNAME').required().asString(),
    },
}

Describe the solution you'd like
I think a general way to solve this would be a general way to configure the default behaviour:

import 'dotenv-defaults/config'

import { config } from 'env-var'

const env = config({ requiredByDefault: false })

export default {
    cms: {
        app: env.get('VIDEOPLAYER__CMS__APP').asString(), // => string
        baseUrl: env.get('VIDEOPLAYER__CMS__BASE_URL').asString(), // => string
        
        password: env.get('VIDEOPLAYER__CMS__PASSWORD').required().asString(),
        username: env.get('VIDEOPLAYER__CMS__USERNAME').required().asString(),
    },
}

Describe alternatives you've considered
This is what I'm currently using, but I think having a general way to configure the default behavior of your package would be more beneficial.

import 'dotenv-defaults/config'

import { get } from 'env-var'

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const getEnv = (name: string, required = false) => get(name).required(required)

export default {
    cms: {
        app: getEnv('VIDEOPLAYER__CMS__APP').asString(),
        baseUrl: getEnv('VIDEOPLAYER__CMS__BASE_URL').asString(),
        password: getEnv('VIDEOPLAYER__CMS__PASSWORD', true).asString(),
        username: getEnv('VIDEOPLAYER__CMS__USERNAME', true).asString(),
    },
}

v8.0.0

Preparing for a v8.0.0 release.

I will push to the typescript-refactor branch in the next few days with a working codebase that handles some or all of these items:

  • Rewrite module in TypeScript
  • Support for non Node.js/React environments #155
  • Deno support #153
  • Support a description function #142
  • Add option to make all variables required by default #147
  • Update documentation
  • Create a migration guide for API changes from v7 to v8
  • Support both ESM and CJS (looks like nearform/lyra is a good example of this)
  • Add docs for Next.js/React usage (issue #162)
  • Add docs for Vite

Expose accessors so they can be composed

Is your feature request related to a problem? Please describe.
I would like to access the internal accessors, so that I can create new ones, while using the built-in accessors internally.

Describe the solution you'd like
Exposing the accessors, like env.accessors.

Describe alternatives you've considered
At the moment, I'm importing lib/accessors/xxx, which is far from ideal, as they are not part of the public API.

Additional context

Example:

        asIntRange: (value, range) => {
            value = asInt(value);

            if (value < range[0] || value >= range[1]) {
                throw new Error(`to be within range [${range[0]},${range[1]}]`);
            }

            return value;
        },

where asInt is the lib/accessors/int.js.

.asUrlString() attaches "/" to url

Hey,

As the title says.
Not sure if this by design or a bug, but I think it's very confusing.

example:

const MY_URL = env.get('BASE_URL').default('https://my.baseurl.com').asUrlString();
console.log(MY_URL); // https://my.baseurl.com/

Property 'required' does not exist on type 'IPresentVariable'. ts(2339)

Describe the bug πŸ›
As title.

To Reproduce πŸ“
Just copy the code from https://github.com/evanshortiss/env-var#variable

const SECRET = env.get('SECRET', 'bad-secret').required(NODE_ENV === 'production').asString()

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
Should not complain about Property 'required' does not exist on type 'IPresentVariable'. ts(2339)

Screenshots πŸ“·
image

Environment (please complete the following information) πŸ’»:

  • OS: macOS
  • Runtime: VS Code
  • Version: 1.41.1

Additional context

// Works fine without setting the default value
const SECRET = env.get('SECRET').required(process.env.NODE_ENV === 'production').asString()

Add 'example/README.md'

Is your feature request related to a problem? Please describe.
Currently, the README.md at the project root is quite long and verbose. For me, and maybe others, it is quite hard to sieve through all the information.

Describe the solution you'd like
I was thinking of creating an 'example/README.md' which will:

  • Provide brief details on the example files available under 'example/'
  • Contain some of the example content shifted over from 'README.md' to this new file
    • With links added to the original 'README.md' at appropriate points leading to their corresponding relevant sections in 'example/README.md'

Describe alternatives you've considered
Refactor the existing 'README.md' and remove less important content. I already did a quick look-through and most details in it should be shown somewhere in my opinion, just maybe shifted to a secondary 'README.md' file for further reference.

Additional context
Let me know what you think!

URL validity

I understand that the library is using is-url under the hook. But I think that package is a bit too simplistic.

EnvVarError: env-var: "DATABASE_URL" should be a valid URL, but was "postgres://admin:password@localhost:5432"

This is a valid URL.

Remove `raiseError` accessor param

relates to #71 (see #72 (comment) )

As a developer, I want my accessor to throw an exception rather than having to call raiseError() when the incoming value is invalid, so that I don't have to think about how env-var's internals work.

Remove the raiseError parameter from accessors. An accessor should be able to simply throw an exception, which generateAccessor()'s inner function can catch and handle appropriately.

Add description() function

Is your feature request related to a problem? Please describe.
It would be helpful to provide a description for each environment variable.

Describe the solution you'd like
A .description('Specifies that and that') function which text may be used in error messages as well as enhance code readability.
Moreover if a .help() function will be implemented (see separate issue) this description text may be used to generate help text.

Client side.

I don't sure if this has been asked before, but can this package access environment variables on the client side? I tried to print the env variable to the console, but it said it was undefined, even though I could see what it printed in the terminal. I know I'm terrible at explaining, but maybe the image i've included may help.

bad-at-explaining

asArray should accept env-variable also with one element

module.exports = function asArray (raiseError, value, delimeter) {
delimeter = delimeter || ','
if (!value.includes(delimeter)) {
raiseError(`should include values separated with the delimeter "${delimeter}"`)
}
return asString(raiseError, value).split(delimeter)
}

Current behaviour:
"value" - throws error because of missing delimiter (bad)
"value," - returns ["value,"] (bad)

Expected result for both cases should be ["value"]. That can be fixed by removing lines 8-10.

Using multiple extra accessors causes type errors arising from union types

Describe the bug πŸ›

In TypeScript, if I attempt to use two extra accessors with different return types, the types for the computed values seem to be a union over the return types of all of the extra accessors, rather than just the return type of the accessor in use.

(Related: those types also include undefined, even in cases where the value is required, which seems wrong β€” but that's a separate issue I guess.)

To Reproduce πŸ“

Here we use the asEmail example given in README.md, and we add another extra accessor called asZero which always returns zero. So the type for asEmail is string, and the type for asZero is number:

process.env.ADMIN = '[email protected]'
 
const asEmail: ExtensionFn<string> = (value) => {
  const split = String(value).split('@')
  if (split.length !== 2) {
    throw new Error('must contain exactly one "@"')
  }
  return value
}

const asZero: ExtensionFn<number> = (value) => 0;

const env = from(process.env, {asEmail, asZero})

Then the type required for a value using either of these extra accessors is number | string | undefined, so I have to write the following:

const validEmail: number | string | undefined = env.get('ADMIN').asEmail()
const zero: number | string | undefined = env.get('ADMIN').asZero()

Expected behaviour πŸ€·β€β™‚οΈπŸ€·

I would expect this to work / not fail type checking:

const validEmail: string | undefined = env.get('ADMIN').asEmail()
const zero: number | undefined = env.get('ADMIN').asZero()

(In fact, as mentioned above, I'd love to not have to include undefined here; AFAICS asEmail always either returns a string or raises an error, so why not...

const validEmail: string = env.get('ADMIN').asEmail()
const zero: number = env.get('ADMIN').asZero()

... but again, that's secondary to the issue I really want to raise, which is the union thing.)

Environment (please complete the following information) πŸ’»:

  • OS: macOS Mojave 10.14.6
  • Runtime: Chrome 79.0.3945.130
  • Version 6.0.4

Feature request: A way to check for empty values

As far as i can tell, there currently doesn't seem to be a way to check if a value is empty or not - which seems like a basic feature for a library like this.

Especially when working with .env.example files you sometimes run into issues where someone might forget to fill out some values - having a way to ensure that the original value (before type coercion) is not empty, would be awesome.

I've tried using a extraAccessors (e.g. asStringNotEmpty()) but the extraAccessors doesn't get called when the property is undefined and extraAccessors are not chainable with required().

I'd be happy to work on a PR for this, but hear some thoughts first as to how to implement this or if you would want to consider implementing this feature at all.

Update Husky configuration

When running pre-commit hooks, the following warning is displayed:

Warning: Setting pre-commit script in package.json > scripts will be deprecated
Please move it to husky.hooks in package.json, a .huskyrc file, or a husky.config.js file
Or run ./node_modules/.bin/husky-upgrade for automatic update

See https://github.com/typicode/husky for usage

Release 6.1.1 to NPM

Many thanks for supporting this package! I was hoping to check if version 6.1.1 will be published to NPM (currently 6.1.0 is published, which causes build failures with Typescript).

Explain chained method in order to implement a verbose mode

I think env-var is brilliant and it is my lightweight 'go-to' solution for (not surprisingly) environment variable reading.

However, one thing I like our node code to do is output what environment variables they are reading, and what values they are using (eg: from process.env or from a supplied default)

It seems a bit pointless putting in code like this every time:
if (process.env.RETRIES) console.log('Reading value for RETRIES from the environment'); const RETRIES = env.get('RETRIES').default(5).asInt(); ...
After struggling with various ways of implementing this in env-var, I came up with the idea for an .explain() method for the chain:
const RETRIES=env.get(''RETRIES).default(5).explain(console.debug).asInt();

If you don't pass anything in the param, then console.debug is the default

I implemented it in variable.js:

    /**
     * Explain what is going on with the variable setting
     * This is useful to people such as Docker admins, who may miss setting environment vars
     * when they should - for example if the use is poorly documented
     * @param {function} out
     */
    explain: function (out) {
      if (!out || typeof out !=='function') out=console.debug;
      let assignedVal = container[varName];
      if (assignedVal) {
        out(`${varName} read from environment, value: ${assignedVal}`);
      } else {
        if (defValue) {
          out(`${varName} not found in environment, using default value: ${defValue}`);
        } else {
          out(`${varName} cannot be read from environment, no default value specified`);
        }
      }
      return accessors
    }

Usage looks like this:

const RETRIES1 = env.get('RETRIES1').explain().asInt();
const RETRIES2 = env.get('RETRIES2').default(7).explain().asInt();
const RETRIES3 = env.get('RETRIES3').explain().asInt();
RETRIES1 read from environment, value: 15
RETRIES2 not found in environment, using default value: 7
RETRIES3 cannot be read from environment, no default value specified

Does this look like a useful feature? Have I implemented it in the best place?

TypeError: env.get is not a function

Describe the bug πŸ›
getting this error when trying to get environment variable using the package

file:///x/index.js:3
const PORT = env.get('PORT').default('5432').asString();
                 ^

TypeError: env.get is not a function
    at file:///private/tmp/test/index.js:3:18
    at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:61:12)

To Reproduce πŸ“
see code snippets added in Additional context

Expected behaviour πŸ€·β€β™‚οΈπŸ€·
variable PORT having value and not crush the proccess

Environment (please complete the following information) πŸ’»:

  • OS: MAC
  • Runtime NodeJS
  • Version 16.15.1

Additional context
index.js

import * as env from "env-var"

const PORT = env.get('PORT').default('5432').asString();
console.log(PORT)

package.json

  "main": "index.js",
  "type": "module",

npm ls

[email protected] /x
└── [email protected]

Ability to override process.env

In a case of using Create-React-App, it seems they are doing something funky with their process variable. That is, if you access process via require or import, it will be null. So this lib will never get the env vars set in by this app.

Would you mind adding an extra param for get(envVar, default, container) that allows us to pass in our own process.env as the 3rd param?

I could do a PR if you prefer that route.

Custom validation extension/custom accessor before `as` check.

Hey!

I know there is already an option to add custom accessors, but those are only going at the end of the evaluation chaing. I'm interested in sort of a middleware approach.

An example scenario would be - We have a .env file like this:

CUSTOM_ENUM=<PLACEHOLDER{identifier}>

For app to be used, placeholder must be replaced with an actual value, which shall be evaluated as a specific enum. So, a custom check is needed in addition to .required() and .asEnum, e.g. something like this:

const placeholderCheck : ExtensionFn<VariableAccessors<unknown>> = (
  accessors: ExtensionFn<VariableAccessors<unknown>>,
  value: string,
): VariableAccessors<unknown> => {
  if (value.match(/^<PLACEHOLDER{.*}>$/)) {
    throw new Error(`placeholder must be replaced with a proper value.`);
  }
  return accessors;
};

const env = from(process.env, { placeholderCheck });
const value = env
      .get('CUSTOM_ENUM')
      .required()
      .placeholderCheck()
      .asEnum<CustomEnum>(['TEST_VALUE_1', 'TEST_VALUE_2']),

The idea is that you want to check the original value first, and then proceed to type assertion. After checking the examples and source code I was not able to find a way to achieve this with the current implementation. If this is indeed possible - it would be nice to update the readme/examples.

Thanks!

An in-range update of @types/node is breaking the build 🚨

The devDependency @types/node was updated from 10.12.8 to 10.12.9.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/node is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).
  • βœ… coverage/coveralls: First build on greenkeeper/@types/node-10.12.9 at 100.0% (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.