Skip to main content

General Node.js usage

Env Core works with any Node.js application, regardless of the framework you’re using. This guide covers general usage patterns that apply to all Node.js projects.

Basic usage

The simplest way to use Env Core is to call validateEnv at the start of your application:
import { validateEnv } from 'env-core';

const env = validateEnv({
  PORT: Number,
  NODE_ENV: String,
  DEBUG: { type: Boolean, default: false },
});

console.log(`Starting application on port ${env.PORT}`);

Organizing your configuration

For larger projects, organize your environment configuration in a dedicated module:
config/env.js
import { validateEnv } from 'env-core';

export const envSchema = {
  // Server configuration
  PORT: Number,
  HOST: { type: String, default: '0.0.0.0' },
  
  // Environment
  NODE_ENV: String,
  
  // Database
  DATABASE_URL: String,
  DB_POOL_SIZE: { type: Number, default: 10 },
  
  // Features
  DEBUG: { type: Boolean, default: false },
  ENABLE_LOGGING: { type: Boolean, default: true },
};

export const env = validateEnv(envSchema);
Then import it where needed:
index.js
import { env } from './config/env.js';
import { startServer } from './server.js';
import { connectDatabase } from './database.js';

await connectDatabase(env.DATABASE_URL);
await startServer(env.PORT, env.HOST);

Using with different module systems

Env Core supports both ESM and CommonJS:
import { validateEnv } from 'env-core';

const env = validateEnv({
  PORT: Number,
  NODE_ENV: String,
});

TypeScript support

For TypeScript projects, Env Core provides full type inference:
config/env.ts
import { validateEnv, type ValidatedEnv, type EnvSchema } from 'env-core';

export const envSchema = {
  PORT: Number,
  NODE_ENV: String,
  DEBUG: { type: Boolean, default: false },
} satisfies EnvSchema;

export const env = validateEnv(envSchema);
export type Env = ValidatedEnv<typeof envSchema>;
index.ts
import { env } from './config/env';

// TypeScript knows the exact types
console.log(env.PORT);      // number
console.log(env.NODE_ENV);  // string
console.log(env.DEBUG);     // boolean

Environment-specific configuration

You can use different .env files for different environments:
const envFile = process.env.NODE_ENV === 'test' ? 'test.env' : '.env';
const env = validateEnv(envSchema, envFile);
Or create environment-specific configuration:
config/env.js
import { validateEnv } from 'env-core';

const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';

export const envSchema = {
  PORT: { type: Number, default: isDevelopment ? 3000 : undefined },
  DEBUG: { type: Boolean, default: isDevelopment },
  LOG_LEVEL: { type: String, default: isDevelopment ? 'debug' : 'info' },
};

export const env = validateEnv(envSchema);

Using with worker threads

When using worker threads, validate the environment in the main thread and pass the validated config to workers:
main.js
import { Worker } from 'worker_threads';
import { env } from './config/env.js';

const worker = new Worker('./worker.js', {
  workerData: {
    port: env.PORT,
    debug: env.DEBUG,
  },
});
worker.js
import { workerData } from 'worker_threads';

console.log(`Worker running with config:`, workerData);

Using with child processes

Child processes inherit environment variables from the parent:
import { spawn } from 'child_process';
import { env } from './config/env.js';

// Validate in parent process
const child = spawn('node', ['child.js'], {
  env: {
    ...process.env,
    PORT: String(env.PORT),
    DEBUG: String(env.DEBUG),
  },
});

Testing patterns

For testing, use a separate test environment file:
test/setup.js
import { validateEnv } from 'env-core';
import { envSchema } from '../config/env.js';

// Use test environment file
export const testEnv = validateEnv(envSchema, 'test.env');
test.env
PORT=4000
NODE_ENV=test
DEBUG=false
DATABASE_URL=postgresql://localhost/test_db
Or mock environment variables in tests:
test/example.test.js
import { beforeAll, afterAll } from '@jest/globals';

let originalEnv;

beforeAll(() => {
  originalEnv = process.env;
  process.env = {
    ...originalEnv,
    PORT: '4000',
    NODE_ENV: 'test',
    DEBUG: 'false',
  };
});

afterAll(() => {
  process.env = originalEnv;
});

CLI applications

For CLI applications, validate environment before running commands:
bin/cli.js
#!/usr/bin/env node

import { validateEnv } from 'env-core';

const env = validateEnv({
  API_KEY: String,
  API_URL: String,
  DEBUG: { type: Boolean, default: false },
});

if (env.DEBUG) {
  console.log('Debug mode enabled');
}

// Run CLI commands with validated environment

Serverless functions

In serverless environments, validate once at cold start:
functions/api.js
import { validateEnv } from 'env-core';

// Validate once at cold start
const env = validateEnv({
  DATABASE_URL: String,
  API_KEY: String,
  DEBUG: { type: Boolean, default: false },
});

// Handler uses validated environment
export async function handler(event, context) {
  if (env.DEBUG) {
    console.log('Event:', event);
  }
  
  // Use env.DATABASE_URL, env.API_KEY, etc.
}

Error handling patterns

Env Core validates at startup, so no error handling is needed in your business logic:
import { env } from './config/env.js';
// If validation fails, process exits here

// This code only runs if validation succeeds
function startApplication() {
  // No need to check if env.PORT exists or is a number
  // Env Core guarantees it's valid
  console.log(`Starting on port ${env.PORT}`);
}

Best practices

Call validateEnv once at application startup, not in every module:
// ✅ Good: Validate once
// config/env.js
export const env = validateEnv(envSchema);

// ❌ Bad: Validate in multiple places
// server.js
const env = validateEnv(envSchema);
// database.js
const env = validateEnv(envSchema);
Export the validated env object from a central module:
// config/env.js
export const env = validateEnv(envSchema);

// Other files
import { env } from './config/env.js';
Add .env to your .gitignore but maintain a .env.example file:
.gitignore
.env
.env.local
.env.example
PORT=3000
NODE_ENV=development
DATABASE_URL=postgresql://localhost/myapp
DEBUG=false
Add comments to your schema explaining each variable:
export const envSchema = {
  // Server port (default: 3000 in dev)
  PORT: Number,
  
  // Environment: development, production, test
  NODE_ENV: String,
  
  // PostgreSQL connection string
  DATABASE_URL: String,
};

Next steps

Express.js integration

See framework-specific integration with Express.js

Examples

Explore more usage examples

Build docs developers (and LLMs) love