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 pageSource-backed Markdown

Run 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.

The same tested bootstrap from the Config lab
ts
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.