🎯

environment-config

🎯Skill

from dadbodgeoff/drift

VibeIndex|
What it does

Validates and centralizes environment variables with type safety, fail-fast startup checks, and multi-environment support.

πŸ“¦

Part of

dadbodgeoff/drift(69 items)

environment-config

Installation

npm installInstall npm package
npm install -g driftdetect
npm installInstall npm package
npm install -g driftdetect@latest
npm installInstall npm package
npm install -g driftdetect-mcp
Claude Desktop ConfigurationAdd this to your claude_desktop_config.json
{ "mcpServers": { "drift": { "command": "driftdetect-mcp" } } ...
πŸ“– Extracted from docs: dadbodgeoff/drift
6Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Centralized environment variable management with validation. Fail fast at startup if config is invalid. Supports multi-environment setups (dev/staging/prod) with type-safe access.

Overview

# Environment Configuration

Centralized, validated environment variables that fail fast at startup.

When to Use This Skill

  • Starting a new project that needs env var management
  • Environment variables scattered across codebase
  • Missing vars causing runtime crashes
  • Need different configs for dev/staging/prod
  • Want type-safe access to configuration

Core Concepts

  1. Centralized config - Single source of truth for all env vars
  2. Fail fast - Validate at startup, not when first accessed
  3. Type safety - Full TypeScript/Python typing for all config values
  4. Environment separation - Clear distinction between dev/staging/prod

File Structure

```

project/

β”œβ”€β”€ .env # Local development (gitignored)

β”œβ”€β”€ .env.example # Template (committed)

β”œβ”€β”€ .env.production # Production overrides (gitignored or in CI)

β”œβ”€β”€ .env.local # Local overrides (gitignored)

└── src/

└── lib/

└── env.ts # Validation and exports

```

TypeScript Implementation

With Zod Validation

```typescript

// lib/env.ts

import { z } from 'zod';

/**

* Server-side environment variables.

* These are NOT exposed to the browser.

*/

const serverSchema = z.object({

NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),

// Database

DATABASE_URL: z.string().url(),

// External services

API_URL: z.string().url().default('http://localhost:8787'),

REDIS_URL: z.string().url().optional(),

// Feature flags

ENABLE_ANALYTICS: z.coerce.boolean().default(false),

ENABLE_RATE_LIMITING: z.coerce.boolean().default(true),

// Secrets

JWT_SECRET: z.string().min(32),

// Logging

LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),

});

/**

* Client-side environment variables.

* MUST be prefixed with NEXT_PUBLIC_ (Next.js) or VITE_ (Vite)

*/

const clientSchema = z.object({

NEXT_PUBLIC_API_URL: z.string().url(),

NEXT_PUBLIC_APP_URL: z.string().url().optional(),

});

// Validate at module load time

const serverEnv = serverSchema.safeParse(process.env);

const clientEnv = clientSchema.safeParse(process.env);

if (!serverEnv.success) {

console.error('❌ Invalid server environment variables:');

console.error(JSON.stringify(serverEnv.error.flatten().fieldErrors, null, 2));

throw new Error('Invalid server environment configuration');

}

if (!clientEnv.success) {

console.error('❌ Invalid client environment variables:');

console.error(JSON.stringify(clientEnv.error.flatten().fieldErrors, null, 2));

throw new Error('Invalid client environment configuration');

}

/**

* Type-safe server environment.

* Use this in API routes and server components.

*/

export const env = serverEnv.data;

/**

* Type-safe client environment.

* Use this in client components.

*/

export const publicEnv = clientEnv.data;

// Type exports

export type Env = z.infer;

export type PublicEnv = z.infer;

```

Without Dependencies (Lightweight)

```typescript

// lib/env.ts - No external dependencies

function requireEnv(key: string): string {

const value = process.env[key];

if (!value) {

throw new Error(Missing required environment variable: ${key});

}

return value;

}

function optionalEnv(key: string, defaultValue: string): string {

return process.env[key] || defaultValue;

}

function boolEnv(key: string, defaultValue: boolean): boolean {

const value = process.env[key];

if (value === undefined) return defaultValue;

return value === 'true' || value === '1';

}

function intEnv(key: string, defaultValue: number): number {

const value = process.env[key];

if (value === undefined) return defaultValue;

const parsed = parseInt(value, 10);

if (isNaN(parsed)) {

throw new Error(Environment variable ${key} must be a number);

}

return parsed;

}

export const env = {

// Required

DATABASE_URL: requireEnv('DATABASE_URL'),

JWT_SECRET: requireEnv('JWT_SECRET'),

// Optional with defaults

API_URL: optionalEnv('API_URL', 'http://localhost:8787'),

LOG_LEVEL: optionalEnv('LOG_LEVEL', 'info'),

PORT: intEnv('PORT', 3000),

// Booleans

ENABLE_ANALYTICS: boolEnv('ENABLE_ANALYTICS', false),

ENABLE_RATE_LIMITING: boolEnv('ENABLE_RATE_LIMITING', true),

// Computed

isDev: process.env.NODE_ENV === 'development',

isProd: process.env.NODE_ENV === 'production',

isTest: process.env.NODE_ENV === 'test',

} as const;

// Validate on import

Object.keys(env); // Triggers all getters

```

Python Implementation

With Pydantic

```python

# config/settings.py

from pydantic_settings import BaseSettings

from pydantic import Field, field_validator

from typing import Literal

from functools import lru_cache

class Settings(BaseSettings):

"""Application settings with validation."""

# Environment

environment: Literal["development", "production", "test"] = "development"

debug: bool = False

# Database

database_url: str = Field(..., description="PostgreSQL connection string")

# External services

api_url: str = "http://localhost:8787"

redis_url: str | None = None

# Feature flags

enable_analytics: bool = False

enable_rate_limiting: bool = True

# Secrets

jwt_secret: str = Field(..., min_length=32)

# Logging

log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"

@field_validator("database_url")

@classmethod

def validate_database_url(cls, v: str) -> str:

if not v.startswith(("postgresql://", "postgres://")):

raise ValueError("database_url must be a PostgreSQL connection string")

return v

@property

def is_dev(self) -> bool:

return self.environment == "development"

@property

def is_prod(self) -> bool:

return self.environment == "production"

class Config:

env_file = ".env"

env_file_encoding = "utf-8"

case_sensitive = False

@lru_cache

def get_settings() -> Settings:

"""Cached settings instance. Call once at startup."""

return Settings()

# Validate on import

settings = get_settings()

```

Without Dependencies

```python

# config/settings.py

import os

from dataclasses import dataclass

from typing import Optional

class ConfigError(Exception):

"""Raised when configuration is invalid."""

pass

def require_env(key: str) -> str:

"""Get required environment variable or raise."""

value = os.getenv(key)

if not value:

raise ConfigError(f"Missing required environment variable: {key}")

return value

def optional_env(key: str, default: str) -> str:

"""Get optional environment variable with default."""

return os.getenv(key, default)

def bool_env(key: str, default: bool) -> bool:

"""Get boolean environment variable."""

value = os.getenv(key)

if value is None:

return default

return value.lower() in ("true", "1", "yes")

def int_env(key: str, default: int) -> int:

"""Get integer environment variable."""

value = os.getenv(key)

if value is None:

return default

try:

return int(value)

except ValueError:

raise ConfigError(f"Environment variable {key} must be an integer")

@dataclass(frozen=True)

class Settings:

"""Immutable application settings."""

# Required

database_url: str

jwt_secret: str

# Optional

api_url: str

log_level: str

port: int

# Feature flags

enable_analytics: bool

enable_rate_limiting: bool

# Computed

is_dev: bool

is_prod: bool

is_test: bool

def load_settings() -> Settings:

"""Load and validate settings from environment."""

return Settings(

database_url=require_env("DATABASE_URL"),

jwt_secret=require_env("JWT_SECRET"),

api_url=optional_env("API_URL", "http://localhost:8787"),

log_level=optional_env("LOG_LEVEL", "INFO"),

port=int_env("PORT", 8000),

enable_analytics=bool_env("ENABLE_ANALYTICS", False),

enable_rate_limiting=bool_env("ENABLE_RATE_LIMITING", True),

is_dev=os.getenv("ENVIRONMENT", "development") == "development",

is_prod=os.getenv("ENVIRONMENT") == "production",

is_test=os.getenv("ENVIRONMENT") == "test",

)

# Validate on import

settings = load_settings()

```

Usage Examples

TypeScript

```typescript

// In API routes

import { env } from '@/lib/env';

export async function GET() {

if (env.ENABLE_ANALYTICS) {

await trackEvent('api_call');

}

const response = await fetch(${env.API_URL}/data);

return Response.json(await response.json());

}

// In client components

import { publicEnv } from '@/lib/env';

const apiClient = createClient(publicEnv.NEXT_PUBLIC_API_URL);

```

Python

```python

# In FastAPI

from config.settings import settings

@app.get("/health")

async def health():

return {

"status": "healthy",

"environment": settings.environment,

"debug": settings.debug,

}

# In services

from config.settings import settings

async def process_data():

if settings.enable_analytics:

await track_event("data_processed")

```

.env.example Template

```bash

# =============================================================================

# Required - App will not start without these

# =============================================================================

DATABASE_URL=postgresql://user:pass@localhost:5432/myapp

JWT_SECRET=your-secret-key-at-least-32-characters-long

# =============================================================================

# External Services

# =============================================================================

API_URL=http://localhost:8787

REDIS_URL=redis://localhost:6379

# =============================================================================

# Feature Flags

# =============================================================================

ENABLE_ANALYTICS=false

ENABLE_RATE_LIMITING=true

# =============================================================================

# App Config

# =============================================================================

NODE_ENV=development

LOG_LEVEL=debug

PORT=3000

```

.gitignore

```gitignore

# Environment files

.env

.env.local

.env.*.local

.env.production

.env.staging

# Keep the example

!.env.example

```

Docker Integration

```dockerfile

# Build args for client-side vars (baked into bundle)

ARG NEXT_PUBLIC_API_URL

ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

# Runtime vars set via docker-compose or k8s

```

```yaml

# docker-compose.yml

services:

app:

build: .

environment:

- NODE_ENV=production

- DATABASE_URL=${DATABASE_URL}

- JWT_SECRET=${JWT_SECRET}

env_file:

- .env.production

```

Best Practices

  1. Validate at startup - Never let invalid config reach runtime
  2. Use typed access - No raw process.env calls in business logic
  3. Separate client/server - Client vars need special prefixes
  4. Default sensibly - Dev-friendly defaults, prod requires explicit config
  5. Document everything - .env.example is your config documentation

Common Mistakes

  • Committing .env files with secrets
  • Using process.env directly throughout codebase
  • Not validating at startup (crashes at 3am instead)
  • Exposing server secrets to client bundle
  • Missing .env.example (new devs can't onboard)

Related Skills

  • [Feature Flags](../feature-flags/)
  • [Error Handling](../error-handling/)

More from this repository10

🎯
file-uploads🎯Skill

Securely validates, scans, and processes file uploads with multi-stage checks, malware detection, and race condition prevention.

🎯
design-tokens🎯Skill

Generates a comprehensive, type-safe design token system with WCAG AA color compliance and multi-framework support for consistent visual design.

🎯
feature-flags🎯Skill

Enables controlled feature rollouts, A/B testing, and selective feature access through configurable flags for gradual deployment and user targeting.

🎯
ai-coaching🎯Skill

Guides users through articulating creative intent by extracting structured parameters and detecting conversation readiness.

🎯
cloud-storage🎯Skill

Enables secure, multi-tenant cloud file storage with signed URLs, direct uploads, and visibility control for user-uploaded assets.

🎯
community-feed🎯Skill

Generates efficient social feed with cursor pagination, trending algorithms, and engagement tracking for infinite scroll experiences.

🎯
email-service🎯Skill

Simplifies email sending, templating, and tracking with robust SMTP integration and support for multiple email providers and transactional workflows.

🎯
error-sanitization🎯Skill

Sanitizes error messages by logging full details server-side while exposing only generic, safe messages to prevent sensitive information leakage.

🎯
batch-processing🎯Skill

Optimizes database operations by collecting and batching independent records, improving throughput by 30-40% with built-in fallback processing.

🎯
rate-limiting🎯Skill

Implements tier-based API rate limiting using a sliding window algorithm with Redis or in-memory storage for fair usage control.