Configuration

Configure your Vercube application

Vercube provides a powerful, type-safe configuration system that lets you customize every aspect of your application. Configuration is defined in a vercube.config.ts file at the root of your project, and can be accessed at runtime throughout your application.

Creating Configuration

Create a vercube.config.ts file in your project root:

vercube.config.ts
import { defineConfig } from '@vercube/core';

export default defineConfig({
  server: {
    port: 3000,
    host: 'localhost'
  },
  logLevel: 'info'
});

The defineConfig function provides TypeScript autocomplete and type checking for all configuration options.

The defineConfig function is a helper that provides type safety and IDE autocomple. It doesn't do anything at runtime - it just returns your configuration object with proper types.

Configuration Options

Server Configuration

Controls how your HTTP server runs:

vercube.config.ts
export default defineConfig({
  server: {
    // Network settings
    host: 'localhost',
    port: 3000,
    
    // HTTPS configuration
    https: false,
    // Or with certificates:
    https: {
      key: './certs/key.pem',
      cert: './certs/cert.pem'
    },
    
    // Static file serving
    static: {
      dirs: ['public'],        // Directories to serve
      maxAge: 3600,            // Cache time in seconds
      immutable: false,        // Set immutable cache header
      etag: true               // Enable ETags
    }
  }
});

Runtime Configuration

Configuration that's accessible at runtime in your application:

vercube.config.ts
export default defineConfig({
  runtime: {
    // Built-in session configuration
    session: {
      secret: process.env.SESSION_SECRET || 'change-me-in-production',
      name: 'vercube_session',
      duration: 60 * 60 * 24 * 7  // 7 days in seconds
    },
    
    // Your custom runtime config (more on this below)
    database: {
      host: 'localhost',
      port: 5432,
      name: 'myapp'
    },
    api: {
      baseUrl: 'https://api.example.com',
      timeout: 5000
    }
  }
});

Build Configuration

Configuration for the Vercube CLI build process:

vercube.config.ts
export default defineConfig({
  build: {
    // Project root directory
    root: process.cwd(),
    
    // Entry point(s)
    entry: 'src/index.ts',
    // Or multiple entries:
    entry: ['src/index.ts', 'src/worker.ts'],
    
    // Build output
    output: {
      dir: 'dist',           // Output directory
      publicDir: 'public'    // Public assets directory
    },
    
    // TypeScript configuration
    tsconfig: './tsconfig.json',
    dts: true,  // Generate .d.ts files
    
    // Build-time defines (injected into code)
    define: {
      'process.env.API_URL': JSON.stringify('https://api.example.com')
    },
    
    // Bundler to use
    bundler: 'rolldown',
    
    // Custom bundler plugins
    plugins: [
      // Your rolldown plugins here
    ]
  }
});

Logging Configuration

vercube.config.ts
export default defineConfig({
  // Log level for the application
  logLevel: 'debug'  // 'debug' | 'info' | 'warn' | 'error' | 'silent'
});

Environment Flags

vercube.config.ts
export default defineConfig({
  // Automatically set based on NODE_ENV, but you can override
  production: process.env.NODE_ENV === 'production',
  dev: process.env.NODE_ENV !== 'production'
});

Accessing Configuration at Runtime

Use the RuntimeConfig class to access your configuration in controllers, services, and other parts of your application:

UserController.ts
import { Controller, Get, Inject } from '@vercube/core';
import { RuntimeConfig } from '@vercube/core';

@Controller('/users')
export class UserController {
  
  @Inject(RuntimeConfig)
  private config!: RuntimeConfig;
  
  @Get('/session-info')
  getSessionInfo() {
    // Access built-in runtime config
    return {
      sessionName: this.config.runtimeConfig?.session?.name,
      sessionDuration: this.config.runtimeConfig?.session?.duration
    };
  }
}
RuntimeConfig.runtimeConfig is always optional (undefined until the app initializes), so always use optional chaining (?.) when accessing it.

Custom Runtime Configuration

You can add your own runtime configuration with full type safety:

Step 1: Define Your Config Type

src/types/AppConfig.ts
export interface AppConfig {
  database: {
    host: string;
    port: number;
    name: string;
  };
  api: {
    baseUrl: string;
    timeout: number;
  };
}

Step 2: Use Type Parameter in defineConfig

vercube.config.ts
import { defineConfig } from '@vercube/core';
import type { AppConfig } from './src/types/AppConfig';

export default defineConfig<AppConfig>({
  runtime: {
    // Built-in session config still works
    session: {
      secret: process.env.SESSION_SECRET!,
      duration: 60 * 60 * 24 * 7
    },
    
    // Your custom configuration with full autocomplete
    database: {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432'),
      name: process.env.DB_NAME || 'myapp'
    },
    api: {
      baseUrl: process.env.API_URL || 'https://api.example.com',
      timeout: 5000
    }
  }
});

Step 3: Access Your Config with Type Safety

DatabaseService.ts
import { RuntimeConfig } from '@vercube/core';
import type { AppConfig } from '../types/AppConfig';

export class DatabaseService {
  @Inject(RuntimeConfig)
  private config!: RuntimeConfig<AppConfig>;
  
  async connect() {
    const dbConfig = this.config.runtimeConfig?.database;
    
    if (!dbConfig) {
      throw new Error('Database configuration not found');
    }
    
    // Full autocomplete for your config!
    console.log(`Connecting to ${dbConfig.host}:${dbConfig.port}/${dbConfig.name}`);
  }
}

Environment Variables

Vercube automatically loads environment variables from .env files using c12.

Basic Usage

Create a .env file in your project root:

.env
NODE_ENV=development
SESSION_SECRET=my-secret-key
DB_HOST=localhost
DB_PORT=5432
API_URL=https://api.example.com

Access in your config:

vercube.config.ts
export default defineConfig({
  runtime: {
    session: {
      secret: process.env.SESSION_SECRET || 'fallback-secret'
    },
    database: {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432')
    }
  }
});

Customizing .env Loading

vercube.config.ts
export default defineConfig({
  c12: {
    dotenv: {
      // Custom .env file path
      fileName: '.env.local',
      
      // Load from specific directory
      cwd: process.cwd()
    }
  }
});

Disabling .env Loading

vercube.config.ts
export default defineConfig({
  c12: {
    dotenv: false  // Disable automatic .env loading
  }
});

Default Configuration

If you don't provide a vercube.config.ts file, Vercube uses these defaults:

{
  logLevel: 'debug',
  production: process.env.NODE_ENV === 'production',
  dev: process.env.NODE_ENV !== 'production',
  
  build: {
    root: process.cwd(),
    entry: 'src/index.ts',
    output: {
      dir: 'dist',
      publicDir: 'public'
    },
    bundler: 'rolldown'
  },
  
  server: {
    runtime: 'node',
    host: 'localhost',
    port: 3000,
    https: false,
    static: {
      dirs: ['public']
    }
  },
  
  runtime: {
    session: {
      secret: '<randomly-generated>',
      name: 'vercube_session',
      duration: 60 * 60 * 24 * 7  // 7 days
    }
  },
  
  experimental: {}
}

You only need to override the values you want to change.

Configuration by Environment

export default defineConfig({
  logLevel: 'debug',
  dev: true,
  server: {
    port: 3000,
    host: 'localhost'
  }
});

Complete Example

Here's a real-world configuration example:

vercube.config.ts
import { defineConfig } from '@vercube/core';
import type { AppConfig } from './src/types/AppConfig';

const isDev = process.env.NODE_ENV !== 'production';

export default defineConfig<AppConfig>({
  // Environment
  production: !isDev,
  dev: isDev,
  logLevel: isDev ? 'debug' : 'info',
  
  // Server
  server: {
    runtime: 'node',
    host: isDev ? 'localhost' : '0.0.0.0',
    port: parseInt(process.env.PORT || '3000'),
    
    https: isDev ? false : {
      key: './certs/privkey.pem',
      cert: './certs/fullchain.pem'
    },
    
    static: {
      dirs: ['public', 'uploads'],
      maxAge: isDev ? 0 : 86400,  // No cache in dev, 1 day in prod
      immutable: !isDev,
      etag: true
    }
  },
  
  // Runtime configuration
  runtime: {
    session: {
      secret: process.env.SESSION_SECRET || 'dev-secret',
      name: 'app_session',
      duration: 60 * 60 * 24 * (isDev ? 7 : 30)  // 7 days dev, 30 days prod
    },
    
    // Custom app config
    database: {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432'),
      name: process.env.DB_NAME || 'myapp',
      ssl: !isDev
    },
    
    redis: {
      host: process.env.REDIS_HOST || 'localhost',
      port: parseInt(process.env.REDIS_PORT || '6379')
    },
    
    api: {
      baseUrl: process.env.API_URL || 'https://api.example.com',
      timeout: 5000,
      retries: 3
    },
    
    features: {
      enableWebSockets: true,
      enableFileUploads: true,
      maxUploadSize: 10 * 1024 * 1024  // 10MB
    }
  },
  
  // Build config (for vercube CLI)
  build: {
    root: process.cwd(),
    entry: 'src/index.ts',
    output: {
      dir: 'dist',
      publicDir: 'public'
    },
    dts: true,
    define: {
      'process.env.BUILD_TIME': JSON.stringify(new Date().toISOString())
    }
  }
});

Troubleshooting

Config is undefined at runtime

Make sure you're injecting RuntimeConfig, not trying to import the config file directly:

// ❌ Wrong - config file can't be imported
import config from './vercube.config';

// ✅ Correct - inject RuntimeConfig
@Inject(RuntimeConfig)
private config!: RuntimeConfig;

TypeScript errors with custom config

Make sure you're passing your type to defineConfig:

// ❌ Missing type parameter
export default defineConfig({
  runtime: {
    myCustomConfig: { ... }  // TypeScript error!
  }
});

// ✅ With type parameter
export default defineConfig<MyConfig>({
  runtime: {
    myCustomConfig: { ... }  // Works!
  }
});

Environment variables not loading

Check that your .env file is in the project root and that dotenv loading is enabled (it is by default).

Previous

Dependency Injection

Understanding and using the Dependency Injection container in Vercube

Next