Resolid Framework applications are configured through the AppOptions interface passed to createApp(). This page covers all available configuration options and best practices.
AppConfig Interface
The core configuration interface defines basic app settings:
export interface AppConfig {
readonly name : string ;
readonly debug ?: boolean ;
readonly timezone ?: string ;
}
Configuration Properties
The unique name of your application. This is accessible via app.name and context.name.
Enable debug mode for additional logging and development features. Accessible via app.debug and context.debug.
The application’s timezone. Sets process.env.timezone and is accessible via app.timezone and context.timezone.
Basic Configuration Example
import { createApp } from '@resolid/core' ;
const app = await createApp ({
name: 'MyApp' ,
debug: process . env . NODE_ENV === 'development' ,
timezone: 'America/New_York' ,
});
console . log ( app . name ); // 'MyApp'
console . log ( app . debug ); // true or false
console . log ( app . timezone ); // 'America/New_York'
AppOptions Interface
AppOptions extends AppConfig with additional configuration for extensions, providers, and exposed services:
export type AppOptions < E extends ExposeSchema = Record < string , never >> = AppConfig & {
readonly extensions ?: ( Extension | ExtensionCreator )[];
readonly providers ?: Provider [];
readonly expose ?: E ;
};
type ExposeSchema = Record < string , Token >;
Extensions
Register extensions to add functionality to your application:
import { createApp , type Extension , type ExtensionCreator } from '@resolid/core' ;
// Static extension
const loggerExtension : Extension = {
name: 'logger' ,
providers: [ /* ... */ ],
};
// Extension creator (with configuration)
const createDbExtension = ( config : DbConfig ) : ExtensionCreator => {
return ( context ) => ({
name: 'database' ,
providers: [ /* ... */ ],
});
};
const app = await createApp ({
name: 'MyApp' ,
extensions: [
loggerExtension ,
createDbExtension ({ host: 'localhost' , port: 5432 }),
],
});
See Extensions for detailed information.
Providers
Register dependency injection providers directly in the app configuration:
import { createApp , type Token } from '@resolid/core' ;
const CONFIG = Symbol ( 'CONFIG' ) as Token < AppConfiguration >;
const LOGGER = Symbol ( 'LOGGER' ) as Token < Logger >;
const app = await createApp ({
name: 'MyApp' ,
providers: [
{
token: CONFIG ,
factory : () => ({
apiUrl: process . env . API_URL ?? 'http://localhost:3000' ,
apiKey: process . env . API_KEY ?? '' ,
}),
},
{
token: LOGGER ,
factory : () => ({
log : ( msg : string ) => console . log ( `[ ${ new Date (). toISOString () } ] ${ msg } ` ),
error : ( msg : string ) => console . error ( `[ ${ new Date (). toISOString () } ] ${ msg } ` ),
}),
},
],
});
Providers defined at the app level are registered after extension providers, so they can override extension-provided services.
See Dependency Injection for detailed information.
Expose
Expose specific services on the app’s $ property for convenient, type-safe access:
const LOGGER = Symbol ( 'LOGGER' ) as Token < Logger >;
const DATABASE = Symbol ( 'DATABASE' ) as Token < Database >;
const USER_SERVICE = Symbol ( 'USER_SERVICE' ) as Token < UserService >;
const app = await createApp ({
name: 'MyApp' ,
providers: [
{ token: LOGGER , factory : () => new Logger () },
{ token: DATABASE , factory : () => new Database () },
{ token: USER_SERVICE , factory : () => new UserService () },
],
expose: {
logger: LOGGER ,
db: DATABASE ,
users: USER_SERVICE ,
},
});
// Type-safe access to exposed services
app . $ . logger . log ( 'Hello!' );
const user = await app . $ . users . findById ( '123' );
await app . $ . db . query ( 'SELECT * FROM users' );
The expose option provides full TypeScript inference. The type of app.$ is automatically inferred from the exposed tokens.
Complete Configuration Example
Here’s a comprehensive example showing all configuration options:
import { createApp , type Extension , type Token } from '@resolid/core' ;
// Define types
interface AppConfig {
apiUrl : string ;
apiKey : string ;
maxRetries : number ;
}
interface Logger {
info ( message : string ) : void ;
error ( message : string , error ?: Error ) : void ;
}
interface Database {
connect () : Promise < void >;
query ( sql : string ) : Promise < unknown []>;
dispose () : Promise < void >;
}
interface UserService {
findById ( id : string ) : Promise < User | null >;
create ( data : CreateUserData ) : Promise < User >;
}
// Create tokens
const CONFIG = Symbol ( 'CONFIG' ) as Token < AppConfig >;
const LOGGER = Symbol ( 'LOGGER' ) as Token < Logger >;
const DATABASE = Symbol ( 'DATABASE' ) as Token < Database >;
const USER_SERVICE = Symbol ( 'USER_SERVICE' ) as Token < UserService >;
// Create extensions
const loggingExtension : Extension = {
name: 'logging' ,
providers: [
{
token: LOGGER ,
factory : () => ({
info : ( msg ) => console . log ( `[INFO] ${ msg } ` ),
error : ( msg , err ) => console . error ( `[ERROR] ${ msg } ` , err ),
}),
},
],
};
const databaseExtension : Extension = {
name: 'database' ,
providers: [
{
token: DATABASE ,
factory : () => {
const logger = inject ( LOGGER );
const config = inject ( CONFIG );
return new DatabaseConnection ( config , logger );
},
},
],
bootstrap : async ( context ) => {
const db = context . container . get ( DATABASE );
await db . connect ();
if ( context . debug ) {
context . emitter . on ( 'app:ready' , () => {
console . log ( 'Database connected and ready' );
});
}
},
};
// Create and configure app
const app = await createApp ({
// Basic configuration
name: 'MyApp' ,
debug: process . env . NODE_ENV === 'development' ,
timezone: process . env . TZ ?? 'UTC' ,
// Extensions
extensions: [
loggingExtension ,
databaseExtension ,
],
// App-level providers
providers: [
{
token: CONFIG ,
factory : () => ({
apiUrl: process . env . API_URL ?? 'http://localhost:3000' ,
apiKey: process . env . API_KEY ?? '' ,
maxRetries: Number ( process . env . MAX_RETRIES ) || 3 ,
}),
},
{
token: USER_SERVICE ,
factory : () => {
const db = inject ( DATABASE );
const logger = inject ( LOGGER );
return new UserService ( db , logger );
},
},
],
// Expose services
expose: {
config: CONFIG ,
logger: LOGGER ,
db: DATABASE ,
users: USER_SERVICE ,
},
});
await app . run ();
// Use exposed services
app . $ . logger . info ( 'Application started' );
app . $ . logger . info ( `API URL: ${ app . $ . config . apiUrl } ` );
const user = await app . $ . users . findById ( '123' );
Environment-Based Configuration
Organize configuration by environment:
interface EnvironmentConfig {
name : string ;
debug : boolean ;
timezone : string ;
database : {
host : string ;
port : number ;
database : string ;
};
api : {
url : string ;
key : string ;
};
}
function getConfig () : EnvironmentConfig {
const env = process . env . NODE_ENV ?? 'development' ;
const configs : Record < string , EnvironmentConfig > = {
development: {
name: 'MyApp-Dev' ,
debug: true ,
timezone: 'UTC' ,
database: {
host: 'localhost' ,
port: 5432 ,
database: 'myapp_dev' ,
},
api: {
url: 'http://localhost:3000' ,
key: 'dev-key' ,
},
},
production: {
name: 'MyApp' ,
debug: false ,
timezone: 'UTC' ,
database: {
host: process . env . DB_HOST ! ,
port: Number ( process . env . DB_PORT ),
database: process . env . DB_NAME ! ,
},
api: {
url: process . env . API_URL ! ,
key: process . env . API_KEY ! ,
},
},
};
return configs [ env ] ?? configs . development ;
}
const config = getConfig ();
const app = await createApp ({
name: config . name ,
debug: config . debug ,
timezone: config . timezone ,
providers: [
{
token: DATABASE_CONFIG ,
factory : () => config . database ,
},
{
token: API_CONFIG ,
factory : () => config . api ,
},
],
});
Configuration Files
Load configuration from external files:
import { readFileSync } from 'node:fs' ;
import { join } from 'node:path' ;
interface AppConfigFile {
app : {
name : string ;
debug : boolean ;
timezone : string ;
};
features : {
enableCache : boolean ;
enableMetrics : boolean ;
};
}
function loadConfig () : AppConfigFile {
const configPath = join ( process . cwd (), 'config' , 'app.json' );
const configFile = readFileSync ( configPath , 'utf-8' );
return JSON . parse ( configFile );
}
const config = loadConfig ();
const app = await createApp ({
name: config . app . name ,
debug: config . app . debug ,
timezone: config . app . timezone ,
providers: [
{
token: FEATURE_FLAGS ,
factory : () => config . features ,
},
],
});
Best Practices
Use Environment Variables Load sensitive configuration (API keys, database credentials) from environment variables, not hardcoded values.
Validate Configuration Validate configuration at startup to fail fast if required values are missing or invalid.
Separate Concerns Use extensions for feature-specific configuration, and app-level providers for shared configuration.
Type Safety Define TypeScript interfaces for your configuration to catch errors at compile time.
Configuration Validation Example
import { z } from 'zod' ;
const ConfigSchema = z . object ({
name: z . string (). min ( 1 ),
debug: z . boolean (). default ( false ),
timezone: z . string (). default ( 'UTC' ),
database: z . object ({
host: z . string (),
port: z . number (). int (). positive (),
database: z . string (),
}),
api: z . object ({
url: z . string (). url (),
key: z . string (). min ( 10 ),
}),
});
function loadAndValidateConfig () {
const rawConfig = {
name: process . env . APP_NAME ,
debug: process . env . DEBUG === 'true' ,
timezone: process . env . TZ ,
database: {
host: process . env . DB_HOST ,
port: Number ( process . env . DB_PORT ),
database: process . env . DB_NAME ,
},
api: {
url: process . env . API_URL ,
key: process . env . API_KEY ,
},
};
try {
return ConfigSchema . parse ( rawConfig );
} catch ( error ) {
console . error ( 'Configuration validation failed:' , error );
process . exit ( 1 );
}
}
const config = loadAndValidateConfig ();
const app = await createApp ({
name: config . name ,
debug: config . debug ,
timezone: config . timezone ,
// ... rest of configuration
});