Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional required env? #78

Open
neezer opened this issue May 5, 2018 · 16 comments
Open

Conditional required env? #78

neezer opened this issue May 5, 2018 · 16 comments

Comments

@neezer
Copy link
Contributor

neezer commented May 5, 2018

Is there a way to conditionally mark ENV vars as required based on the values of other ENV vars?

In my case, I have the following 4 vars:

  • USE_TLS
  • TLS_CA_CERT_PATH
  • TLS_CERT_PATH
  • TLS_KEY_PATH

If USE_TLS is true, then I want the other three flagged as required. If it is false, then the other three should be optional.

@SimenB
Copy link
Collaborator

SimenB commented May 6, 2018

Ooooh, I like that idea! That's not supported now, but I for one would love to see a PR for it 🙂

@neezer
Copy link
Contributor Author

neezer commented May 7, 2018

What about this?

envalid.cleanEnv(process.env, {
  USE_TLS: envalid.bool(),
  TLS_CA_CERT_PATH: envalid.str({
    requiredWhen: 'USE_TLS' // is true
  }),
  // ...
})
  • Should the subject of requiredWhen always have to be a boolean? Or should this work for any truthy value?
  • Should requiredWhen allow multiple requirements?

@af
Copy link
Owner

af commented May 8, 2018

Great feature idea, I could see this being pretty useful. I worry that an approach like requiredWhen that takes a string would be a bit too restrictive though. Maybe requiredWhen could point to a function that takes the full raw env object? eg:

envalid.cleanEnv(process.env, {
  USE_TLS: envalid.bool(),
  TLS_CA_CERT_PATH: envalid.str({
    requiredWhen: rawEnv => !!rawEnv.USE_TLS
  }),
  // ...
})

@neezer
Copy link
Contributor Author

neezer commented May 8, 2018

Maybe requiredWhen could point to a function that takes the full raw env object?

Seems like it would probably be simpler to implement and answers both of my previous questions too, so that's a 👍 .

neezer added a commit to neezer/envalid that referenced this issue May 8, 2018
neezer added a commit to neezer/envalid that referenced this issue May 8, 2018
@antoine-pous
Copy link
Contributor

Any news about this awesome feature? :)

@rainum
Copy link

rainum commented Sep 3, 2019

@antoine-pous check #81 PR

@antoine-pous
Copy link
Contributor

antoine-pous commented Sep 5, 2019

@rainum that doesn't answer to my question, actually the feature is not merged and seem inactive from months. In a professional context i can't use a commit from a PR in progress :(

@veeramarni
Copy link

Seems to be a nice feature. I think we need to send a new PR on it.

@clarkey
Copy link

clarkey commented Nov 17, 2020

This would be an awesome feature 👍

@af
Copy link
Owner

af commented Jan 28, 2021

Something like the requiredWhen approach above could still be viable if we pass in the raw env. I'm not sure how we could do it in a type-safe way though.

Something like this is possible already though using TypeScript and the json validator, if you can deal with having the related env vars stored together in json:

import * as e from 'envalid'

// Read env vars from .env
require('dotenv').config()

type TlsJsonType = {
  USE_TLS: false,
} | {
  USE_TLS: true,
  TLS_CA_CERT_PATH: string,
  TLS_CERT_PATH: string,
  TLS_KEY_PATH: string,
}

const env = e.cleanEnv(process.env, {
  // TS type-checks that this default value works with the union type
  TLS_CONFIG: e.json<TlsJsonType>({ default: { USE_TLS: false } }),
})

if (env.TLS_CONFIG.USE_TLS) {
  // Within this guard clause, TS knows this is a string
  console.log(env.TLS_CONFIG.TLS_KEY_PATH)
}

Of course, the json() validator won't validate that particular shape at runtime, but you could create a custom validator that does that work there as well

@dbrenot-pelmorex
Copy link

bumping this issue years later. Any progress?

@gitSambhal
Copy link

Any update on this?

@af
Copy link
Owner

af commented Apr 7, 2024

No updates, sorry– I think the approach in this comment is still doable: #78 (comment)

I haven't had the time to add this but I'd welcome a PR 👍

@gitSambhal
Copy link

gitSambhal commented Apr 12, 2024

const requiredIfEnabledSqlQListener = makeValidator((x) => {
  const isEnabled = convertToBoolean(process.env. ENABLE_RAPTOR_SQL_LISTENER);
  if (isEnabled && !x) {
    throw new Error("required when ENABLE_RAPTOR_SQL_LISTENER is enabled");
  }
  return x;
});

const schema = {
  ENABLE_RAPTOR_SQL_LISTENER: bool({ default: false }),
  RAPTOR_SQL_DB: requiredIfEnabledSqlQListener(),
  RAPTOR_SQL_IS_SSL: requiredIfEnabledSqlQListener(),
};

This is how I did it, remember to either put all the env variables in .env file to trigger the validation or set these as empty null
otherwise the environment variables will be undefined and the custom validation will not run.

@TiagoGouvea
Copy link

@gitSambhal I'm stuck on this.

Does that code work?

I can't manage to make it work here, I just need to have one optional env variable.

I did it work like this, but it not look a good approach:

import { cleanEnv, str, port, bool, url, makeValidator } from 'envalid';
import dotenv from 'dotenv';
dotenv.config();

const nodeEnv = process.env.NODE_ENV;
const isStagProd = ['production', 'staging'].includes(nodeEnv);
console.log('isStagProd', isStagProd);

const specs = {
  DATABASE_URL: url(),
  MY_OPTIONAL: str(),
};

if (!isStagProd) {
  // @ts-ignore
  delete specs.MY_OPTIONAL;
}

export const env = cleanEnv(process.env, specs);

Can someone help me with a better solution?

@gitSambhal
Copy link

gitSambhal commented Jul 7, 2024

Hi @TiagoGouvea
You check the below code based on my understanding of your requirements.
You can modify as per your requirement.

import { cleanEnv, str, port, bool, url, makeValidator } from 'envalid';
import dotenv from 'dotenv';
dotenv.config();

const myOptionalValidator = makeValidator((x) => {
  const nodeEnv = process.env.NODE_ENV;
  const isStagProd = ['production', 'staging'].includes(nodeEnv);
  console.log('isStagProd', isStagProd);
  if (!isStagProd) {
    // don't validate here, just return the value
    return x;
  }
  if(!x) {
    throw new Error('The value is required')
  }
  return x;
});

const specs = {
  DATABASE_URL: url(),
  MY_OPTIONAL: myOptionalValidator(),
};

export const env = cleanEnv(process.env, specs);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants