Config Guide
Why config matters as a backend scope resource
In Vona, configuration is not only a global backend settings concern. It is also a module-scoped resource that can be defined close to a module and then accessed through scope.
That matters because backend capabilities often need both:
- reusable module-local defaults
- project-level overrides for the current application
- runtime-sensitive configuration chosen by mode, flavor, and instance
Initialize the config skeleton
Example: initialize config for module demo-student.
npm run vona :init:config demo-studentThis gives the module its own config file under the module’s config area.
Define module config
Representative pattern:
export function config(_app: VonaApplication) {
return {
title: 'Hello World',
};
}The important point is that module config fields are declared directly, and the framework extracts the config typing from that shape.
Use config within the current module
The current module’s config can be accessed through scope.
Representative pattern:
console.log(this.scope.config.title);This keeps config access aligned with the same module-resource model used for service, model, entity, locale, and error.
Use config across modules
Cross-module config access uses the cross-module scope surface.
Representative pattern:
console.log(this.$scope.demoStudent.config.title);A practical distinction is:
this.scope.configfor the current modulethis.$scope.<module>.configfor another module
Project config is layered, not single-file
At the application level, Vona config is assembled from a cascading set of config files.
In the current repo, representative project config files include:
config.tsconfig.dev.tsconfig.dev.play.tsconfig.test.tsconfig.prod.tsconfig.dev.local.tsconfig.test.local.tsconfig.prod.local.tsconfig.local.tsx
That means effective config is built by merging general config with more specific runtime/flavor/local layers.
A practical mental model is:
- start from the shared base config
- merge mode-specific config
- merge flavor-specific config when present
- merge local override config last
This is one of the main reasons backend config should be understood together with Runtime and Flavors.
Async config loading is normal
In the current Vona startup flow, project config items are loaded asynchronously and then deep-merged.
That means async config is a normal part of the framework model rather than a special workaround.
A practical implication is:
- config can safely derive values from runtime information, filesystem state, or other async setup needs during config assembly
How runtime env and flavor affect config
Runtime metadata does not only choose env files. It also participates directly in config assembly.
In the current app config, representative fields derived from env/meta include:
config.meta.modeconfig.meta.flavorconfig.server.workersconfig.server.listen.*config.server.serve.*config.database.defaultClient- logger, Redis, and mail client settings
That means config selection and runtime selection are part of one continuous flow rather than two unrelated systems.
Override module config at the project level
Project-level config can override module-level defaults.
Representative pattern:
config.modules = {
'demo-student': {
title: 'Hello World!!',
},
};This is the key ownership rule:
- module config defines reusable defaults close to the module
- project config overrides those defaults for the current backend application
App config vs instance-merged config
One important Vona distinction is:
this.app.configis the global application configthis.ctx.configis the effective config for the current instance-aware request context
That matters in multi-instance or multi-tenant scenarios.
In the current runtime, instance config is assembled by merging:
- the global app config
- any static config under
config.instance.instances[instanceName].config - any stored instance config data from the instance record
A practical interpretation is:
- use
app.configwhen you need the backend-wide config baseline - use
ctx.configwhen behavior should respect the active instance
Config access surfaces at a glance
| Access surface | Typical meaning |
|---|---|
app.config | app-wide baseline config |
ctx.config | effective config for the active instance-aware context |
this.scope.config | current-module config resource |
this.$scope.<module>.config | another module’s config resource |
A practical rule is:
- use
scopeaccess when the task is about module-owned config - use
app.configwhen the task is about backend-wide baseline behavior - use
ctx.configwhen request-scoped behavior must respect the active instance
Effective instance-aware config merge order
This page owns the config-layering view. For env-file precedence and mode/flavor selection, see Runtime and Flavors. For the fuller request-context view of instance resolution and instance-aware config behavior, see Multi-Instance and Instance Resolution.
In the current runtime, the effective instance-aware config can be understood at a high level like this:
- start from the app-wide baseline config
- merge static config from
config.instance.instances[instanceName].config - merge any persisted config stored on the instance record
- expose the merged result as
ctx.config
That means ctx.config is not just a pointer to app.config. It is the instance-aware effective config surface.
Instance config shape
Representative instance config shape in the current repo looks like:
config.instance = {
getInstanceName: undefined,
queryField: $protocolKey('x-vona-instance-name'),
headerField: $protocolKey('x-vona-instance-name'),
instances: {
'': { password: '', title: '' },
'isolateTest': {
password: '',
title: '',
id: 1000,
isolate: true,
isolateClient: 'isolateTest',
},
},
};The key point is that instance configuration is part of the config system itself, not an external plugin layer.
Config ownership layers
A useful backend mental model is:
- env files provide deployable runtime values
- project config translates and organizes those values into backend config
- module config provides reusable module-local defaults
config.modules[...]overrides module-local defaults for the current appconfig.instance.instances[...]adds static per-instance behaviorctx.configis the effective instance-aware result seen by request-scoped code
This layered model is the safest way to reason about config changes in Cabloy.
Relationship to startup, model, and datasource behavior
Config does not live in isolation. In many backend tasks, config interacts directly with:
- runtime environment and flavor
- startup lifecycle and startup bean overrides
- model options
- datasource defaults and isolated-instance routing
Read this guide together with:
- Backend Essentials
- Backend Foundation
- Runtime and Flavors
- Backend Startup Guide
- Model Guide
- Multi-Database and Datasource Guide
Implementation checks for backend configuration changes
When editing backend configuration behavior, ask:
- should this value live in env files, project config, or module config?
- is the value consumed through current-module scope, cross-module scope,
app.config, orctx.config? - does the config depend on runtime mode, flavor, or instance?
- is there already a module config skeleton or project config layer that should be extended instead of inventing a new pattern?
- will a config change alter startup, datasource, or model behavior indirectly?
That helps AI keep backend configuration aligned with Vona’s module-resource model and current runtime layering.