How do I read node environment variables in TypeScript?
If i use process.env.NODE_ENV
I have this error :
Property 'NODE_ENV' does not exist on type 'ProcessEnv'
I have installed @types/node
but it didn't help.
How do I read node environment variables in TypeScript?
If i use process.env.NODE_ENV
I have this error :
Property 'NODE_ENV' does not exist on type 'ProcessEnv'
I have installed @types/node
but it didn't help.
Once you have installed @types/node
in your project, you can tell TypeScript exactly what variables are present in your process.env
:
environment.d.ts
declare global {
namespace NodeJS {
interface ProcessEnv {
GITHUB_AUTH_TOKEN: string;
NODE_ENV: 'development' | 'production';
PORT?: string;
PWD: string;
}
}
}
// If this file has no import/export statements (i.e. is a script)
// convert it into a module by adding an empty export statement.
export {}
Usage:
process.env.GITHUB_AUTH_TOKEN; // $ExpectType string
This method will give you IntelliSense, and it also takes advantage of string literal types.
Note: the snippet above is module augmentation. Files containing module augmentation must be modules (as opposed to scripts). The difference between modules and scripts is that modules have at least one import/export statement.
In order to make TypeScript treat your file as a module, just add one import statement to it. It can be anything. Even
export {}
will do.
There's no guarantee of what (if any) environment variables are going to be available in a Node process - the NODE_ENV
variable is just a convention that was popularised by Express, rather than something built in to Node itself. As such, it wouldn't really make sense for it to be included in the type definitions. Instead, they define process.env
like this:
export interface ProcessEnv {
[key: string]: string | undefined
}
Which means that process.env
can be indexed with a string in order to get a string back (or undefined
, if the variable isn't set). To fix your error, you'll have to use the index syntax:
let env = process.env["NODE_ENV"];
Alternatively, as jcalz pointed out in the comments, if you're using TypeScript 2.2 or newer, you can access indexable types like the one defined above using the dot syntax - in which case, your code should just work as is.
just add before use process.env.NODE_ENV
follow lines:
declare var process : {
env: {
NODE_ENV: string
}
}
You can use a Type Assertion for this
Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.
Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.
Example
const nodeEnv: string = (process.env.NODE_ENV as string);
console.log(nodeEnv);
Alternatively you might find a library such as env-var more suitable for this specific purpose --
"solution for loading and sanitizing environment variables in node.js with correct typings"
.env
file# Contents of .env file
AUTHENTICATION_API_URL="http://localhost:4000/login"
GRAPHQL_API_URL="http://localhost:4000/graphql"
.env
file into process.env
with dotenv
We can leverage dotenv
to set environment-specific process.env
variables. Create a file called config.ts
in your src/
directory and populate as follows:
// Contents of src/config.ts
import {config as configDotenv} from 'dotenv'
import {resolve} from 'path'
switch(process.env.NODE_ENV) {
case "development":
console.log("Environment is 'development'")
configDotenv({
path: resolve(__dirname, "../.env.development")
})
break
case "test":
configDotenv({
path: resolve(__dirname, "../.env.test")
})
break
// Add 'staging' and 'production' cases here as well!
default:
throw new Error(`'NODE_ENV' ${process.env.NODE_ENV} is not handled!`)
}
Note: This file needs to get imported in your top-most file, likely your src/index.ts via import './config'
(placed before all other imports)
IProcessEnv
After combining a few methods above, we can add some runtime checks for sanity to guarantee that our declared IProcessEnv
interface reflects what ENV variables are set in our .env.*
files. The contents below can also live in src/config.ts
// More content in config.ts
const throwIfNot = function<T, K extends keyof T>(obj: Partial<T>, prop: K, msg?: string): T[K] {
if(obj[prop] === undefined || obj[prop] === null){
throw new Error(msg || `Environment is missing variable ${prop}`)
} else {
return obj[prop] as T[K]
}
}
// Validate that we have our expected ENV variables defined!
['AUTHENTICATION_API_URL', 'GRAPHQL_API_URL'].forEach(v => {
throwIfNot(process.env, v)
})
export interface IProcessEnv {
AUTHENTICATION_API_URL: string
GRAPHQL_API_URL: string
}
declare global {
namespace NodeJS {
interface ProcessEnv extends IProcessEnv { }
}
}
This will give us proper IntelliSense/tslint type checking, as well as some sanity when deploying to various environments.
Note that this also works for a ReactJS app (as opposed to a NodeJS server app). You can omit Step (2) because this is handled by create-react-app
.
After executing with typescript latest version:
npm install --save @types/node
you can use process.env
directly.
console.log(process.env["NODE_ENV"])
you will see the expected result if you have set NODE_ENV
.
what worked for me is that everywhere I want to use process.env
I first import dotenv
and call config()
on it. Also, remember to append !
at the end and ensure the attribute is defined in your .env
file
import dotenv from 'dotenv';
dotenv.config();
export const YOUR_ATTRIBUTE = process.env.YOUR_ATTRIBUTE!;
Here is a short function which is guaranteed to pull the process.env value as a string -- or to throw an error otherwise.
For something more powerful (but also bigger), others here have suggested env-var.
/**
* Returns value stored in environment variable with the given `name`.
* Throws Error if no such variable or if variable undefined; thus ensuring type-safety.
* @param name - name of variable to fetch from this process's environment.
*/
export function env(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing: process.env['${name}'].`);
}
return value;
}
You should then be able to write code like:
let currentEnvironment: string;
currentEnvironment = env('NODE_ENV');
I know this will help someone who searches for this and can't find the simple answer to why your proccess.env
variables are making your compiler whine:
Install @types/node:
npm i @types/node
Then where ever you're including your env as a string, do this:
process.env.YOUR_ENV ?? ''
The double question marks allow you to check for null/undefined.
npm i @types/node
"types": [ "node" ]
to your tsconfig.json file in the compilerSection
section.create a file like global.d.ts
declare global {
namespace NodeJS {
interface ProcessEnv {
SECRET: string;
}
}
}
export {};
Complementing previous responses and after some time with this problem, even installing @types/node, I found this answer. In short, just run a reload window:
"...Although, you probably have to restart typescript language server if it still uses previous version of the tsconfig. In order to do this in VS Code, you do Ctrl+Shift+P
and Reload Window
or TypeScript: Restart TS server
if available..."
here's my solution with envalid (validating and accessing environment variables in Node.js)
import { str, cleanEnv } from 'envalid'
const env = cleanEnv(process.env, {
clientId: str(),
clientSecret: str(),
})
// and now the env is validated and no longer undefined
const clientId = env.clientId
For anyone coming here looking for an answer for Create React App projects specifically, your variable names should start with REACT_APP_
Read more here: https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables
Just typecast the process.env.YOUR_VAR
Example:
mongoose
.connect(String(process.env.MONGO_URL), {
useNewUrlParser: true,
useFindAndModify: false
})
.then(() => console.log('DB connected'))
.catch((err: any) => console.error(err));
The best and easiest way to use node process.env in your typescript project is to first compile with tsc
then run the compiled javascript file with node supplying your ENV
var. Example (first make sure tsconfig.ts
is what you want for the output directory also the name of compiled file, I am using dist
as output directory and index.js
as example):
cd my-typescriptproject
tsc
NODE_ENV=test node ./dist/index.js
Important note: if you have a web app and you are using webpack.DefinePlugin to define process.env
on your window, then these are they typings you are looking for:
declare namespace process {
let env: {
// this is optional, if you want to allow also
// other values than the ones listed below, they will have type
// string | undefined, which is the default
[key: string]: string
commit_hash: string
build_time: string
stage: string
version: string
// ... etc.
}
}
You could also use a type guard function. Something like this that has a return type of
parameterName is string
e.g.
function isEnvVarSpecified(envVar: string | undefined): envVar is string {
if(envVar === undefined || envVar === null) {
return false;
}
if(typeof envVar !== 'string'){
return false;
}
return true;
}
You can then call this as a type guard:
function myFunc() {
if(!isEnvVarSpecified(process.env.SOME_ENV_VAR')){
throw new Error('process.env.SOME_ENV_VAR not found')
}
// From this point on the ts compiler won't complain about
// process.env.SOME_ENV_VAR being potentially undefined
}
I wrote a module to simplify this. It has no dependencies so it's reasonably lightweight. It also works with dotenv, and you can pass a custom process.env
to the env.from
function if you need to.
It's mentioned in a few answers already, but here's an example:
Install it using yarn/npm:
npm install env-var --save
Then read variables:
import * as env from 'env-var'
// Read NODE_ENV and verify that:
// 1) it is set using the required() function
// 2) it is either 'dev' or 'prod'
// 3) throw a runtime exception if conditions #1 or #2 fail
const environment = env.get('NODE_ENV').required().asEnum(['dev', 'prod'])
// Intellisense will suggest 'dev' or 'prod'
if (environment === 'dev') {
console.log('yep, this is dev')
} else {
console.log('looks like this is prod')
}
Or another:
import { get } from 'env-var'
// Read the GitHub token. It could be undefined
const githubToken = get('GITHUB_TOKEN').asString()
// Read MAX_CONCURRENCY, or default to 5. Throw an error if it's
// not set to a positive integer value
const concurrencyLimit = get('MAX_CONCURRENCY').default(5).asIntPositive()
function callGitApi (token: string, concurrency: number) { /* implementation */ }
// TS Error: Argument of type 'string | undefined' is not assignable to
// parameter of type 'string'.
callGitApi(githubToken, concurrencyLimit)
I found that deliberately changing the path to the .env file was my issue as detailed here: https://stackoverflow.com/a/62288163/3605990
tl;dr
module:
import * as dotenv from "dotenv";
dotenv.config({ path: __dirname+'/.env' });
or commonjs:
require('dotenv').config({ path: __dirname+'/.env' });