Claude
Skills
Sign in
Back

config-manager

Included with Lifetime
$97 forever

Expert guide for managing application configuration including environment variables, config files, secrets management, and multi-environment setups. Use when handling .env files, config validation, or configuration architecture.

General

What this skill does


# Configuration Manager Skill

## Overview

This skill helps you design and implement robust configuration management for applications. Covers environment variables, config files, secrets management, validation, type safety, and multi-environment deployments.

## Configuration Philosophy

### Twelve-Factor App Principles
1. **Store config in the environment**: Separate config from code
2. **Strict separation**: Config that varies between deploys should be in env vars
3. **No secrets in code**: Ever. Period.

### Configuration Hierarchy
```
Priority (highest to lowest):
1. Command-line arguments
2. Environment variables
3. Environment-specific config files (.env.local)
4. Default config files (.env)
5. Application defaults in code
```

### What Goes Where
- **Environment Variables**: API keys, database URLs, feature flags
- **Config Files**: Non-sensitive defaults, complex structures
- **Secrets Manager**: Production credentials, API tokens
- **Code**: Application logic, never secrets

## Environment Variables

### .env File Structure

```bash
# .env.example - Commit this file as documentation
# Copy to .env and fill in values

# ===================
# Application
# ===================
NODE_ENV=development
PORT=3000
APP_URL=http://localhost:3000

# ===================
# Database
# ===================
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
DATABASE_POOL_SIZE=10

# ===================
# Authentication
# ===================
# Get from Supabase Dashboard > Settings > API
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# ===================
# External Services
# ===================
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx

# ===================
# Email
# ===================
RESEND_API_KEY=re_xxx
[email protected]

# ===================
# Feature Flags
# ===================
ENABLE_ANALYTICS=true
ENABLE_BETA_FEATURES=false
```

### .gitignore Configuration

```gitignore
# Environment files
.env
.env.local
.env.*.local
.env.development.local
.env.test.local
.env.production.local

# Keep example file
!.env.example
```

### Multi-Environment Setup

```bash
# .env.development
NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/myapp_dev
LOG_LEVEL=debug

# .env.test
NODE_ENV=test
DATABASE_URL=postgresql://localhost:5432/myapp_test
LOG_LEVEL=error

# .env.production
NODE_ENV=production
DATABASE_URL=${DATABASE_URL}  # Set in deployment platform
LOG_LEVEL=info
```

## Type-Safe Configuration

### Zod Schema Validation

```typescript
// src/config/env.ts
import { z } from 'zod';

// Define schema
const envSchema = z.object({
  // Node environment
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),

  // Server
  PORT: z.coerce.number().default(3000),
  APP_URL: z.string().url(),

  // Database
  DATABASE_URL: z.string().url(),
  DATABASE_POOL_SIZE: z.coerce.number().min(1).max(100).default(10),

  // Supabase
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),

  // Stripe (optional in development)
  STRIPE_SECRET_KEY: z.string().startsWith('sk_').optional(),
  STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_').optional(),

  // Feature flags
  ENABLE_ANALYTICS: z.coerce.boolean().default(false),
  ENABLE_BETA_FEATURES: z.coerce.boolean().default(false),
});

// Type export
export type Env = z.infer<typeof envSchema>;

// Parse and validate
function loadEnv(): Env {
  const result = envSchema.safeParse(process.env);

  if (!result.success) {
    console.error('Invalid environment variables:');
    console.error(result.error.format());
    process.exit(1);
  }

  return result.data;
}

// Singleton export
export const env = loadEnv();
```

### Environment Validation at Build Time

```typescript
// src/config/validate-env.ts
import { z } from 'zod';

// Client-side env (exposed to browser)
const clientEnvSchema = z.object({
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

// Server-side env (never exposed to browser)
const serverEnvSchema = z.object({
  DATABASE_URL: z.string(),
  SUPABASE_SERVICE_ROLE_KEY: z.string(),
  STRIPE_SECRET_KEY: z.string().optional(),
});

// Combined
const envSchema = clientEnvSchema.merge(serverEnvSchema);

export function validateEnv() {
  // Only validate server-side env on server
  if (typeof window !== 'undefined') {
    return clientEnvSchema.parse({
      NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
      NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
      NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
    });
  }

  return envSchema.parse(process.env);
}
```

### T3 Env Pattern (Next.js)

```typescript
// src/env.mjs
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';

export const env = createEnv({
  /**
   * Server-side environment variables
   */
  server: {
    DATABASE_URL: z.string().url(),
    NODE_ENV: z.enum(['development', 'test', 'production']),
    SUPABASE_SERVICE_ROLE_KEY: z.string(),
    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  },

  /**
   * Client-side environment variables (exposed to browser)
   * Prefix with NEXT_PUBLIC_
   */
  client: {
    NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
    NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
    NEXT_PUBLIC_APP_URL: z.string().url(),
  },

  /**
   * Runtime values
   */
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    NODE_ENV: process.env.NODE_ENV,
    SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
  },

  /**
   * Skip validation in certain environments
   */
  skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
```

## Configuration Files

### JSON Configuration

```typescript
// config/default.json
{
  "app": {
    "name": "My Application",
    "version": "1.0.0"
  },
  "server": {
    "port": 3000,
    "host": "localhost"
  },
  "cache": {
    "ttl": 3600,
    "maxSize": 1000
  },
  "features": {
    "darkMode": true,
    "betaFeatures": false
  }
}

// config/production.json (overrides default)
{
  "server": {
    "host": "0.0.0.0"
  },
  "cache": {
    "ttl": 86400
  }
}
```

### Config Loader

```typescript
// src/config/loader.ts
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { z } from 'zod';

const configSchema = z.object({
  app: z.object({
    name: z.string(),
    version: z.string(),
  }),
  server: z.object({
    port: z.number(),
    host: z.string(),
  }),
  cache: z.object({
    ttl: z.number(),
    maxSize: z.number(),
  }),
  features: z.object({
    darkMode: z.boolean(),
    betaFeatures: z.boolean(),
  }),
});

type Config = z.infer<typeof configSchema>;

function deepMerge(target: any, source: any): any {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
      result[key] = deepMerge(result[key] || {}, source[key]);
    } else {
      result[key] = source[key];
    }
  }

  return result;
}

export function loadConfig(): Config {
  const env = process.env.NODE_ENV || 'development';
  const configDir = join(process.cwd(), 'config');

  // Load default config
  const defaultPath = join(configDir, 'default.json');
  let config = JSON.parse(readFileSync(defaultPath, 'utf-8'));

  // Merge environment-specific config
  const envPath = join(configDir, `${env}.json`);
  if (existsSync(envPath)) {
    const envC
Files: 1
Size: 15.1 KB
Complexity: 19/100
Category: General

Related in General