📦
Bunty

Configuration

Bunty provides type-safe, schema-validated environment configuration with support for hot-reloading via mounted config files. Schema validation is built into @bunty/common - no extra packages needed.

Basic Usage

Use the env() function to read environment variables with schema validation:

import { env, t } from "@bunty/common";

// Define your configuration with type-safe schemas
const dbUrl = env(
    "DB_URL",
    t.String()
        .Default("postgres://postgres:password@localhost:5432/bunty_dev")
        .Required()
        .Description("Database connection URL")
);

const port = env(
    "PORT",
    t.Number()
        .Default(3000)
        .Min(1)
        .Max(65535)
        .Description("Server port")
);

const nodeEnv = env(
    "NODE_ENV",
    t.Enum(["development", "production", "test"])
        .Default("development")
        .Description("Application environment")
);

// Use your config
console.log(`Connecting to database: ${dbUrl}`);
console.log(`Server will run on port: ${port}`);

📚 Schema Types Reference

The t schema builder is built into @bunty/common. For a complete reference of all available schema types and validators, see the Schema Types documentation.

Common Configuration Patterns

String

const apiKey = env(
    "API_KEY",
    t.String()
        .Required()
        .MinLength(32)
        .MaxLength(128)
        .Pattern(/^[A-Za-z0-9_-]+$/)
        .Description("External API authentication key")
);

const appName = env(
    "APP_NAME",
    t.String()
        .Default("Bunty App")
        .Description("Application display name")
);

Number

const maxConnections = env(
    "MAX_CONNECTIONS",
    t.Number()
        .Default(100)
        .Min(1)
        .Max(1000)
        .Integer()
        .Description("Maximum database connections")
);

const timeout = env(
    "REQUEST_TIMEOUT",
    t.Number()
        .Default(30000)
        .Min(1000)
        .Description("Request timeout in milliseconds")
);

Boolean

const enableCaching = env(
    "ENABLE_CACHING",
    t.Boolean()
        .Default(true)
        .Description("Enable response caching")
);

const debugMode = env(
    "DEBUG",
    t.Boolean()
        .Default(false)
        .Description("Enable debug logging")
);

Enum

const logLevel = env(
    "LOG_LEVEL",
    t.Enum(["debug", "info", "warn", "error"])
        .Default("info")
        .Description("Logging level")
);

const dbDialect = env(
    "DB_DIALECT",
    t.Enum(["mysql", "postgres", "sqlite"])
        .Required()
        .Description("Database dialect")
);

URL

const redisUrl = env(
    "REDIS_URL",
    t.URL()
        .Default("redis://localhost:6379")
        .Description("Redis connection URL")
);

const apiEndpoint = env(
    "API_ENDPOINT",
    t.URL()
        .Required()
        .Secure() // Must be HTTPS
        .Description("External API endpoint")
);

JSON

const features = env(
    "FEATURE_FLAGS",
    t.JSON()
        .Default({ newUI: false, betaFeatures: false })
        .Description("Feature flags configuration")
);

const corsOrigins = env(
    "CORS_ORIGINS",
    t.JSON()
        .Default(["http://localhost:3000"])
        .Description("Allowed CORS origins")
);

Environment Files

Create a .env file in your project root:

# Database
DB_URL=postgres://postgres:secret@localhost:5432/bunty_prod
DB_DIALECT=postgres
MAX_CONNECTIONS=50

# Server
PORT=8080
NODE_ENV=production
API_KEY=sk_live_abc123def456xyz789

# Features
ENABLE_CACHING=true
DEBUG=false
LOG_LEVEL=info

# External Services
REDIS_URL=redis://localhost:6379
API_ENDPOINT=https://api.example.com

# JSON Config
FEATURE_FLAGS={"newUI":true,"betaFeatures":false}
CORS_ORIGINS=["https://app.example.com","https://www.example.com"]

Note: Bun automatically loads .env files - no additional setup needed!

Configuration Object

Organize your config into a single object for easy import:

// src/config/index.ts
import { env, t } from "@bunty/common";

export const config = {
    // Server
    server: {
        port: env("PORT", t.Number().Default(3000)),
        host: env("HOST", t.String().Default("localhost")),
        env: env("NODE_ENV", t.Enum(["development", "production", "test"]).Default("development")),
    },
    
    // Database
    database: {
        url: env("DB_URL", t.String().Required().Description("Database connection URL")),
        maxConnections: env("MAX_CONNECTIONS", t.Number().Default(100).Min(1).Max(1000)),
        ssl: env("DB_SSL", t.Boolean().Default(false)),
    },
    
    // Logging
    logging: {
        level: env("LOG_LEVEL", t.Enum(["debug", "info", "warn", "error"]).Default("info")),
        pretty: env("LOG_PRETTY", t.Boolean().Default(true)),
    },
    
    // Security
    security: {
        apiKey: env("API_KEY", t.String().Required().MinLength(32)),
        corsOrigins: env("CORS_ORIGINS", t.JSON().Default(["*"])),
        jwtSecret: env("JWT_SECRET", t.String().Required().MinLength(64)),
    },
    
    // Features
    features: {
        caching: env("ENABLE_CACHING", t.Boolean().Default(true)),
        rateLimit: env("ENABLE_RATE_LIMIT", t.Boolean().Default(true)),
        debug: env("DEBUG", t.Boolean().Default(false)),
    },
};

Use throughout your app:

import { config } from "./config";

const app = createApp();

app.listen(config.server.port, () => {
    console.log(`Server running on ${config.server.host}:${config.server.port}`);
    console.log(`Environment: ${config.server.env}`);
});

Hot-Reloading with Mounted Config Files

Bunty supports hot-reloading via mounted configuration files - perfect for Docker/Kubernetes deployments:

Mount a Config File

import { watchConfig } from "@bunty/config";

// Watch a mounted config file for changes
const configWatcher = watchConfig("/etc/bunty/config.json", (newConfig) => {
    console.log("Configuration updated:", newConfig);
    
    // Update your application config
    Object.assign(config, newConfig);
    
    // Trigger reconnections, refresh caches, etc.
    reconnectDatabase();
});

// Stop watching when app shuts down
process.on("SIGTERM", () => {
    configWatcher.stop();
});

Mounted Config Example

/etc/bunty/config.json:

{
    "database": {
        "maxConnections": 200,
        "ssl": true
    },
    "features": {
        "newUI": true,
        "betaFeatures": false
    },
    "logging": {
        "level": "warn"
    }
}

When this file changes, your application automatically picks up the new values without restart!

Merge Strategies

import { watchConfig, MergeStrategy } from "@bunty/config";

// Deep merge (default)
watchConfig("/etc/bunty/config.json", onUpdate, {
    strategy: MergeStrategy.Deep
});

// Shallow merge
watchConfig("/etc/bunty/config.json", onUpdate, {
    strategy: MergeStrategy.Shallow
});

// Replace entire config
watchConfig("/etc/bunty/config.json", onUpdate, {
    strategy: MergeStrategy.Replace
});

Validation Errors

Bunty config provides clear, actionable error messages:

// Missing required variable
env("API_KEY", t.String().Required())
// ❌ Error: Environment variable "API_KEY" is required but not set

// Invalid type
env("PORT", t.Number())
// ❌ Error: Environment variable "PORT" must be a number, got "abc"

// Out of range
env("MAX_CONNECTIONS", t.Number().Min(1).Max(1000))
// ❌ Error: Environment variable "MAX_CONNECTIONS" must be between 1 and 1000, got 5000

// Invalid enum value
env("LOG_LEVEL", t.Enum(["debug", "info", "warn", "error"]))
// ❌ Error: Environment variable "LOG_LEVEL" must be one of: debug, info, warn, error. Got: trace

Best Practices

  1. Always validate - Use schemas for all environment variables
  2. Provide defaults - Make local development easy with sensible defaults
  3. Add descriptions - Help developers understand what each variable does
  4. Never commit secrets - Use .env.example as a template, .env in .gitignore
  5. Fail fast - Load all config at startup, not lazily during requests
  6. Use type inference - Let TypeScript infer types from your schemas
  7. Document requirements - List all required env vars in README

.env.example Template

Create a .env.example file to help other developers:

# Server Configuration
PORT=3000
HOST=localhost
NODE_ENV=development

# Database
DB_URL=postgres://postgres:password@localhost:5432/bunty_dev
DB_DIALECT=postgres
MAX_CONNECTIONS=100
DB_SSL=false

# Logging
LOG_LEVEL=info
LOG_PRETTY=true
DEBUG=false

# Security (REQUIRED in production)
API_KEY=your-secret-api-key-min-32-chars
JWT_SECRET=your-jwt-secret-min-64-chars-long

# External Services
REDIS_URL=redis://localhost:6379

# Features
ENABLE_CACHING=true
ENABLE_RATE_LIMIT=true

# CORS
CORS_ORIGINS=["http://localhost:3000"]

⚠️ Security Warning

Never commit .env files or secrets to version control. Use .env.example as a template and keep actual secrets in environment variables or secret management systems.

Next Steps

Have questions? Join our Discord community
Found an issue? Edit this page on GitHub