Configure the application runtime
Keep application settings explicit. Boot one App, give each service its options and leave HTTP and worker startup to the host.
On this page
Source-backed MarkdownRun a small configuration example
The Config walkthrough boots a real App without a database or listener. Copy its two files, run the command, change a port or page size, then trigger a configuration error. Start there before wiring more services.
Repository values are not service options
Use config for application values and the configuration sections consumed by Cache, Auth, Media and Security. Pass storage, url, queue, log, serializer, auth, db and dbOptions through their supported App options.
For example, config.notes.pageSize is yours to read; url.baseUrl constructs the URL service. Do not assume every nested config key configures a same-named service.
import 'dotenv/config';
import { pathToFileURL } from 'node:url';
import type { EnvSource } from '@db3.ai/app/config';
import { App } from '@db3.ai/app/server';
import { loadNotesConfig } from './loadNotesConfig';
/**
* Boots configuration and URL generation without opening HTTP or SQL connections.
*
* @param source - App settings; no process-global environment changes are needed.
* @returns A deliberately selected public summary, never the complete config.
*/
export async function runConfig(source: EnvSource = process.env) {
const notes = loadNotesConfig(source);
const application = new App({
config: { notes },
url: { baseUrl: `http://localhost:${notes.port}` },
dbOptions: { syncColumns: false },
});
try {
return {
name: application.config.get<string>('notes.name'),
port: notes.port,
notesUrl: application.url.to('/notes'),
pageSize: notes.pageSize,
debug: notes.debug,
allowedOrigins: notes.allowedOrigins,
missingValue: application.config.get('notes.missing', 'fallback'),
hasWebhook: application.config.has('notes.webhook'),
webhookEnabled: notes.webhook.enabled,
configReused: application.config === application.config,
};
} finally {
await application.close();
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
console.log(JSON.stringify(await runConfig(), null, 2));
}
Give the application its own database
The generated starter owns its model registry and committed migrations. Keep dbOptions.syncColumns: false for migration-owned schemas. Run migrations explicitly, not each time an HTTP request or worker starts.
Normal model code uses the active App database. Do not pass optional database connections through feature functions. A test or transaction can supply its deliberate connection through App construction or ActiveRecord.withDb().
Boot once and close what you own
Constructing App makes it process-global. Do not create it per HTTP request; use request context for users and request IDs. Service getters reuse the same service instance.
App does not listen for HTTP or install signal handlers. Your host starts and closes HTTP, drains workers, closes custom transports and then closes App. The first HTTP app demonstrates that boundary.
Test the configuration before booting the feature
Pass a plain environment object to your config loader. Assert defaults, invalid values and missing required settings. Then instantiate the real App with those options and close it in finally. The Config page supplies the exact tests and command.
Coverage and reference
This page connects the tested Config example to the existing App and starter workflows. It is not a second implementation or a full dependency-injection system. Custom service lifecycle and production process supervision remain application responsibilities.