Skip to main content

Configuration

Configuration is the foundation of every application. From database connections and authentication providers to cache drivers and external services, nearly every part of your application relies on configuration values. Bejibun provides a centralized configuration system that allows you to:
  • Organize application settings
  • Separate configuration from business logic
  • Manage environment-specific values
  • Access settings consistently throughout your application
  • Keep sensitive information out of source code
This guide explains how configuration works and how to structure it effectively.

Configuration Philosophy

Configuration should be predictable, maintainable, and environment-aware. A common mistake is hardcoding values directly into application code. Avoid:
const apiUrl =
  "https://api.example.com";

const apiKey =
  "secret-key";
Instead:
const apiUrl =
  Config.get("services.api.url");

const apiKey =
  Config.get("services.api.key");
This makes applications easier to maintain and deploy across different environments.

The Config Directory

All application configuration files are stored inside the config directory. Example:
config/
├── app.ts
├── database.ts
├── auth.ts
├── cache.ts
├── storage.ts
└── services.ts
Each file should be responsible for a single area of configuration.

Creating Configuration Files

A configuration file exports a configuration object. Example:
export default {
  name: "My Application",

  env: "development",

  debug: true
};
File:
config/app.ts
This configuration can then be accessed anywhere in the application.

Accessing Configuration Values

Use the Config service to retrieve values.
Config.get("app.name");
Result:
My Application
Nested values can also be accessed. Example:
Config.get(
  "database.connections.mysql.host"
);
This allows configuration to remain organized even in large applications.

Configuration Structure

A typical configuration file might look like:
export default {
  name: "Bejibun",

  debug: true,

  timezone: "UTC",

  locale: "en"
};
Access values using dot notation:
Config.get("app.timezone");

Environment Variables

Configuration files often depend on environment variables. Example:
export default {
  port: Env.get("PORT", 3000),

  host: Env.get("HOST", "0.0.0.0")
};
Environment variables allow applications to behave differently depending on where they are running. Examples:
EnvironmentPurpose
DevelopmentLocal development
TestingAutomated tests
StagingPre-production validation
ProductionLive applications

Default Values

Always provide sensible defaults when appropriate. Example:
Env.get("PORT", 3000);
If the environment variable does not exist:
3000
is returned. This improves developer experience and reduces setup friction.

Application Configuration

The application configuration file contains general settings. Example:
export default {
  name: Env.get(
    "APP_NAME",
    "Bejibun"
  ),

  env: Env.get(
    "APP_ENV",
    "development"
  ),

  debug: Env.get(
    "APP_DEBUG",
    true
  )
};
Common settings include:
  • Application name
  • Environment
  • Debug mode
  • Timezone
  • Locale

Database Configuration

Database connections are typically defined inside config/database.ts. Example:
export default {
  default: "mysql",

  connections: {
    mysql: {
      host: Env.get("DB_HOST"),

      port: Env.get("DB_PORT"),

      database: Env.get("DB_DATABASE"),

      username: Env.get("DB_USERNAME"),

      password: Env.get("DB_PASSWORD")
    }
  }
};
This separates connection details from application code.

Authentication Configuration

Authentication settings belong in a dedicated configuration file. Example:
export default {
  default: "jwt",

  guards: {
    jwt: {
      driver: "jwt"
    }
  }
};
Keeping authentication settings centralized simplifies maintenance and auditing.

Cache Configuration

Cache drivers and related settings can be configured separately. Example:
export default {
  default: "redis",

  stores: {
    redis: {
      host: Env.get("REDIS_HOST")
    }
  }
};
This allows cache implementations to change without affecting application logic.

Storage Configuration

Storage configuration defines where files are stored. Example:
export default {
  default: "local",

  disks: {
    local: {
      root: "./storage"
    }
  }
};
Future environments may switch to cloud storage without changing application code.

Service Configuration

External integrations should have their own configuration file. Example:
export default {
  stripe: {
    key: Env.get(
      "STRIPE_KEY"
    )
  },

  mail: {
    apiKey: Env.get(
      "MAIL_API_KEY"
    )
  }
};
This pattern keeps third-party service settings organized.

Environment-Based Configuration

Different environments often require different values. Development:
APP_ENV=development

APP_DEBUG=true
Production:
APP_ENV=production

APP_DEBUG=false
Configuration files can adapt automatically based on the current environment. Example:
const isProduction =
  Env.get("APP_ENV")
  === "production";

Typed Configuration

One of the advantages of TypeScript is the ability to create strongly typed configuration. Example:
export interface AppConfig {
  name: string;

  env: string;

  debug: boolean;
}
Typed configuration provides:
  • Autocomplete
  • Compile-time validation
  • Better editor support
  • Safer refactoring
Whenever possible, configuration should remain type-safe.

Organizing Large Applications

As applications grow, configuration files can become extensive. Instead of:
config/
└── app.ts
Use:
config/
├── app.ts
├── auth.ts
├── cache.ts
├── database.ts
├── mail.ts
├── queue.ts
├── services.ts
└── storage.ts
Each file should focus on a single concern.

Avoiding Common Mistakes

Hardcoding Secrets

Avoid:
const apiKey =
  "my-secret-key";
Use:
Env.get("API_KEY");
Secrets should never be committed to version control.

Accessing Environment Variables Everywhere

Avoid:
Env.get("DB_HOST");

Env.get("DB_PORT");

Env.get("DB_DATABASE");
throughout your application. Instead:
Config.get(
  "database.connections.mysql"
);
Environment variables should typically be consumed by configuration files, not business logic.

Mixing Business Logic and Configuration

Avoid:
if (Env.get("APP_ENV") === "production") {
  // application logic
}
Business logic should remain separate from configuration concerns whenever possible.

Configuration Caching

As applications grow, repeatedly loading configuration can become inefficient. Future framework versions may provide configuration caching. Example:
bun ace config:cache
Benefits include:
  • Faster startup
  • Reduced file access
  • Improved production performance
If available, configuration caches should be regenerated after configuration changes.

Recommended Practices

Follow these guidelines when working with configuration:
  • Keep configuration centralized
  • Use environment variables for secrets
  • Provide sensible defaults
  • Create dedicated configuration files
  • Prefer type-safe configuration
  • Avoid hardcoded values
  • Separate configuration from business logic
These practices improve maintainability and reduce deployment issues.

Example Configuration Flow

The following diagram illustrates how configuration values are loaded.
.env


config/*.ts


Config Service


Application Code
This layered approach keeps applications organized and easy to maintain.

What’s Next?

Now that you understand how configuration works, the next step is learning how environment variables are loaded and managed. Continue to:
  • Environment Variables
  • Request Lifecycle
In the next chapter, you’ll learn how Bejibun loads environment values, validates them, and uses them throughout the application.