#How to parse process.env

14 messages · Page 1 of 1 (latest)

stoic linden
#

So I have the following:

for (const [key, value] of Object.entries(process.env)) {
  if (value === undefined)
    throw new Error(`process.env.${key} must be provided.`);
}

export default process.env as Required<typeof process.env>;

But when I do:

parsedEnv.something

TS still tells me that parsedEnv.something could be undefined. What's the solution here?

oak matrix
#

Just because the values that exist on process.env are not undefined, doesn't mean something exists.

lunar fox
#

you probably want something more like this:

const isNonNullable = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null

export const getEnvOrThrow = <T extends string>(envName: T) => {
    const env = process.env[envName]
    if (!isNonNullable(env))
        throw new Error(`Required environment variable ${envName} was not provided`)
    return env
}
oak matrix
#

Alternatively if you have a known list of env you want to access, you can do the check/conversion upfront and return a strongly typed env to use instead.

#

If your app already uses a validator/parser library like zod, you can use that as well.

#

(If your app doesn't, I wouldn't bring in a whole dependency just for env though)

lunar fox
#

I also use this version fairly often which can extract multiple props and will throw if any of them arent provided:

export const getEnvsOrThrow = <const T extends string>(envNames: readonly T[]) =>
    Object.fromEntries(envNames.map(envName => {
        const env = process.env[envName]
        if (!isNonNullable(env))
            throw new Error(`Required environment variable ${envName} was not provided`)
        return [envName, env] as const
    })) as { [K in TEnv]: string }

(unfortunately requires a cast for the unhelpful Object.fromEntries return type, but should still be type safe within the context of that function)

stoic linden
#

I have the following types:

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      MONGO_DB: string;
      JWT_SECRET: string;
      NODE_ENV: 'dev' | 'staging' | 'production';
    }
  }
}

export {};

So it would not be possible to get process.env.something because something doesn't exists in the type definition, unless of course you ignore the type error that will come out.

The thing is, even if I try to process.env.JWT_SECRET, and run typescript check, ts will still throw an error like string | undefined is not assignable to type string because somehow process.env.JWT_SECRET is still being treated as string | undefined, even though I have explicit check (the for loop above) that ensures it will never have undefined values, I even used Required which I thought would ensure that Partial is undone.

young flintBOT
#
nonspicyburrito#0

Preview:```ts
import 'node'

declare global {
namespace NodeJS {
interface ProcessEnv {
MONGO_DB: string
JWT_SECRET: string
NODE_ENV: 'dev' | 'staging' | 'production'
}
}
}

process.env.JWT_SECRET
// ^?
...```

oak matrix
#

Seems to work just fine.

oak matrix
#

{ foo: 'hello', bar: 'world' } will pass the check of your original code, but obviously JWT_SECRET doesn't exist on that object and trying to access it will still give you undefined.

young flintBOT
#
nonspicyburrito#0

Preview:```ts
import "node"

const mongoDb = process.env.MONGO_DB
if (!mongoDb)
throw new Error("Missing process.env.MONGO_DB")

const jwtSecret = process.env.JWT_SECRET
if (!jwtSecret)
throw new Error("Missing process.env.JWT_SECRET")

const nodeEnv = process.env.NODE_ENV
...```

oak matrix
#

If you don't have a validator library, I would just write your code like this, and import env wherever you need it.