While working on a TypeScript project, I needed to utilize elements such as API keys, database URLs, and feature flags. At first, I thought I could simply add them directly into my code, but that proved difficult, especially when switching between development and production.
While searching for a better way to manage environment variables in my TypeScript app, I came to know that TypeScript doesn’t have a built-in method to handle them.
In this article, I’ll show you four simple and effective ways to use environment variables in a TypeScript project.
Method 1 – Using dotenv Package
The dotenv package is the most popular way to handle environment variables in TypeScript projects. It loads environment variables from a .env file into process.env.
Here’s how to set it up:
- Install the required packages:
npm install dotenv
npm install @types/node --save-dev- Create a .env file in your project root:
API_KEY=your_api_key_here
DATABASE_URL=mongodb://localhost:27017/myapp
ENABLE_FEATURE_X=true- Load the variables in your TypeScript code:
import * as dotenv from 'dotenv';
dotenv.config();
// Now you can access your variables
const apiKey = process.env.API_KEY;
const dbUrl = process.env.DATABASE_URL;
const featureXEnabled = process.env.ENABLE_FEATURE_X === 'true';
console.log(`API Key: ${apiKey}`);
console.log(`Database URL: ${dbUrl}`);
console.log(`Feature X Enabled: ${featureXEnabled}`);Output:

The dotenv package will automatically load the variables from your .env file, making them available through process.env.
Remember to add your .env file to .gitignore to avoid committing sensitive information to your repository.
Check out: Convert TypeScript Objects to JSON
Method 2 – Using process.env Directly
If you’re developing a Node.js application with TypeScript, you can access environment variables directly through the process.env object, which is available globally.
- Install the Node.js types if you haven’t:
npm install @types/node --save-dev- Create type definitions for your environment variables:
// env.d.ts
export {};
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
PORT?: string;
API_KEY: string;
DATABASE_URL: string;
}
}
}- Set environment variables when running your application:
API_KEY=your_key DATABASE_URL=your_url npm start- Access the variables in your code:
// index.ts
import * as dotenv from 'dotenv';
dotenv.config();
// Access environment variables with type safety
const apiKey = process.env.API_KEY;
const dbUrl = process.env.DATABASE_URL;
const port = process.env.PORT || '3000';
const isDevelopment = process.env.NODE_ENV === 'development';
console.log(`Running in ${process.env.NODE_ENV} mode on port ${port}`);
console.log(`API Key: ${apiKey}`);
console.log(`Database URL: ${dbUrl}`);Output:

This method provides type safety and IntelliSense support for your environment variables, which helps catch errors during development.
Check out: Deep Clone an Object in TypeScript
Method 3 – Using Environment Configuration Files
Another approach is to create separate configuration files for different environments. This is particularly useful for complex applications with many environment-specific settings.
- Create a config directory with environment-specific files:
// config/default.ts
export default {
port: 3000,
apiUrl: 'https://api.example.com',
database: {
host: 'localhost',
port: 5432,
name: 'myapp'
}
};
// config/development.ts
export default {
port: 3000,
apiUrl: 'https://dev-api.example.com',
database: {
host: 'localhost',
port: 5432,
name: 'myapp_dev'
}
};
// config/production.ts
export default {
port: 80,
apiUrl: 'https://api.example.com',
database: {
host: 'db.example.com',
port: 5432,
name: 'myapp_prod'
}
};- Create a configuration loader:
// config/index.ts
import defaultConfig from './default';
import developmentConfig from './development';
import productionConfig from './production';
type Config = typeof defaultConfig;
function getConfig(): Config {
switch (process.env.NODE_ENV) {
case 'development':
return { ...defaultConfig, ...developmentConfig };
case 'production':
return { ...defaultConfig, ...productionConfig };
default:
return defaultConfig;
}
}
export const config = getConfig();- Use the configuration in your application:
import { config } from './config';
console.log(`Server running on port ${config.port}`);
console.log(`Using API at ${config.apiUrl}`);
console.log(`Connected to database ${config.database.name}`);This approach provides strong typing and organizes your configuration in a structured way, making it easier to manage as your application grows.
Check out: Function Overloading in TypeScript
Method 4 – Using cross-env for Cross-Platform Support
When you need to set environment variables in npm scripts that work across different operating systems (especially Windows), the cross-env package is extremely useful.
- Install cross-env:
npm install cross-env --save-dev- Update your package.json scripts:
{
"scripts": {
"start": "cross-env NODE_ENV=production ts-node src/index.ts",
"dev": "cross-env NODE_ENV=development ts-node src/index.ts",
"test": "cross-env NODE_ENV=test jest"
}
}- Access environment variables in your code:
// src/index.ts
console.log(`Running in ${process.env.NODE_ENV} environment`);
if (process.env.NODE_ENV === 'development') {
console.log('Debug mode enabled');
}Output:

This method is particularly useful when working in teams with developers on different operating systems, as it ensures environment variables are set consistently regardless of platform.
Check out: Call REST APIs Using TypeScript
Best Practices for TypeScript Environment Variables
After working with environment variables in various TypeScript projects, I’ve found these practices to be most effective:
- Use strong typing: Define interfaces for your environment variables to get TypeScript’s type checking benefits.
- Set default values: Always provide fallbacks for optional environment variables.
const port = parseInt(process.env.PORT || '3000', 10);
const logLevel = process.env.LOG_LEVEL || 'info';- Validate environment variables: Check that required variables are present at startup:
function validateEnv() {
const requiredEnvVars = ['API_KEY', 'DATABASE_URL'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
}
// Call this when your app starts
validateEnv();- Keep sensitive data out of version control: Always add .env files to your .gitignore.
- Document required environment variables: Create a sample .env.example file with dummy values to show what variables are needed.
Check out: Optional Parameters in TypeScript Interfaces
I hope you found this article helpful. Each method has its strengths, and you can choose the one that best fits your project’s needs. For simple applications, dotenv is often sufficient, while larger projects might benefit from the configuration file approach.
You may also like to read:

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.