Skip to content

Config Usage

How to use @grest-ts/config in your application.

Defining Configuration

typescript
import {GGConfig, GGSetting, GGSecret, GGResource} from "@grest-ts/config"
import {tString, tPosInt, tBoolean} from "@grest-ts/schema"

export const AppConfig = GGConfig.define('/app/', () => ({

    // Settings - general application configuration
    port: new GGSetting('server/port', 3000, 'HTTP server port', tPosInt),
    debug: new GGSetting('debug', false, 'Enable debug mode', tBoolean),

    // Secrets - sensitive values (API keys, passwords)
    apiKey: new GGSecret('external/apiKey', '', 'Third-party API key', tString),
    dbPassword: new GGSecret('db/password', '', 'Database password', tString),

    // Resources - infrastructure configuration
    dbHost: new GGResource('db/host', 'localhost', 'Database host', tString),
    redisUrl: new GGResource('redis/url', 'redis://localhost:6379', 'Redis URL', tString)
}));

Key Types

TypeMethodUse Case
GGSetting.get()General configuration (ports, flags)
GGSecret.reveal()Sensitive data (API keys, passwords)
GGResource.get()Infrastructure (URLs, hosts, paths)

Reading Values

typescript
// Settings
const port = AppConfig.port.get();
const debug = AppConfig.debug.get();

// Secrets - use reveal() to make intent clear
const apiKey = AppConfig.apiKey.reveal();

// Resources
const dbHost = AppConfig.dbHost.get();

Validation

All keys require a validator. Values are validated on read:

typescript
import {tString, tPosInt, tEnum} from "@grest-ts/schema"

// Number validation
port: new GGSetting('port', 8080, 'Server port', tPosInt)

// String validation
name: new GGSetting('name', 'default', 'App name', tString)

// Enum validation
env: new GGSetting('env', 'dev', 'Environment', tEnum(['dev', 'staging', 'prod']))

Watching for Changes

React to configuration changes at runtime:

typescript
const unwatch = AppConfig.port.watch((newPort) => {
    console.log('Port changed to:', newPort);
    // Restart server, update connections, etc.
});

// Later: stop watching
unwatch();

Storage Backends

Configure where each key type reads values from:

typescript
import {GGConfigLoader, GGConfigStoreFile} from "@grest-ts/config"
import {GGSetting, GGSecret, GGResource} from "@grest-ts/config"

new GGConfigLoader(new Map([
    [GGSetting, new GGConfigStoreFile('settings.json', import.meta.url)],
    [GGSecret, new GGConfigStoreEnv()],  // Custom: read from environment
    [GGResource, new GGConfigStoreFile('resources.json', import.meta.url)]
]));

File-based Configuration

Example settings.json:

json
{
    "app": {
        "server": {
            "port": 8080
        },
        "debug": true
    }
}

The key path /app/server/port maps to app.server.port in JSON.

Best Practices

typescript
export const AppConfig = GGConfig.define('/app/', () => ({
    server: {
        port: new GGSetting('server/port', 3000, 'HTTP port', tPosInt),
        host: new GGSetting('server/host', '0.0.0.0', 'Bind address', tString)
    },
    database: {
        host: new GGResource('db/host', 'localhost', 'DB host', tString),
        password: new GGSecret('db/password', '', 'DB password', tString)
    }
}));

// Access
AppConfig.server.port.get();
AppConfig.database.password.reveal();

Provide Meaningful Defaults

typescript
// Good - sensible defaults for local development
port: new GGSetting('port', 3000, 'Server port', tPosInt)
dbHost: new GGResource('db/host', 'localhost', 'Database host', tString)

// Good - empty default for required secrets
apiKey: new GGSecret('apiKey', '', 'Required API key', tString)

Use Descriptive Names

typescript
// Good
new GGSetting('server/port', 3000, 'HTTP server listening port', tPosInt)

// Avoid
new GGSetting('p', 3000, '', tPosInt)