Core Package API
Package: @onebun/core
OneBunApplication
Main application class that bootstraps and runs the HTTP server.
Constructor
new OneBunApplication(
moduleClass: new (...args: unknown[]) => object,
options?: Partial<ApplicationOptions>
)ApplicationOptions
interface ApplicationOptions {
/** Application name for metrics/tracing labels */
name?: string;
/** Port to listen on
* Priority: explicit option > PORT env variable > default (3000)
*/
port?: number;
/** Host to listen on
* Priority: explicit option > HOST env variable > default ('0.0.0.0')
*/
host?: string;
/** Maximum idle time (seconds) before the server closes a connection.
* A connection is idle when no data is sent or received.
* Set to 0 to disable. Default: 120.
*/
idleTimeout?: number;
/** Base path prefix for all routes (e.g., '/api/v1') */
basePath?: string;
/** Route prefix to prepend to all routes (typically service name) */
routePrefix?: string;
/** Enable development mode (default: NODE_ENV !== 'production') */
development?: boolean;
/** Logger configuration options.
* Provides a declarative way to configure logging.
* Priority: loggerLayer > loggerOptions > LOG_LEVEL/LOG_FORMAT env > NODE_ENV defaults
*/
loggerOptions?: LoggerOptions;
/** Custom logger layer (advanced, takes precedence over loggerOptions) */
loggerLayer?: Layer.Layer<Logger>;
/** Environment configuration schema */
envSchema?: TypedEnvSchema;
/** Environment loading options */
envOptions?: {
envFilePath?: string;
loadDotEnv?: boolean;
envOverridesDotEnv?: boolean;
strict?: boolean;
defaultArraySeparator?: string;
valueOverrides?: Record<string, string | number | boolean>;
};
/** Metrics configuration */
metrics?: MetricsOptions;
/** Tracing configuration */
tracing?: TracingOptions;
/** WebSocket configuration */
websocket?: WebSocketApplicationOptions;
/** Static file serving: serve files from a directory for requests not matched by API routes */
static?: StaticApplicationOptions;
/**
* Application-wide middleware class constructors applied to every route
* before module-level, controller-level and route-level middleware.
* Classes must extend BaseMiddleware. DI is fully supported.
* Execution order: global → module → controller → route → handler.
* See Controllers API — Middleware for details.
*/
middleware?: MiddlewareClass[];
/** Enable graceful shutdown on SIGTERM/SIGINT (default: true) */
gracefulShutdown?: boolean;
/** Global exception filters. Route/controller filters take priority. */
filters?: ExceptionFilter[];
/**
* CORS shorthand — auto-prepends CorsMiddleware.
* Pass `true` for permissive defaults, or a CorsOptions object for custom config.
* See Security Middleware for details.
*/
cors?: CorsOptions | true;
/**
* Rate limiting shorthand — auto-prepends RateLimitMiddleware.
* Pass `true` for defaults (100 req / 60s, in-memory), or a RateLimitOptions object.
* See Security Middleware for details.
*/
rateLimit?: RateLimitOptions | true;
/**
* Security headers shorthand — auto-appends SecurityHeadersMiddleware.
* Pass `true` for all defaults, or a SecurityHeadersOptions object.
* See Security Middleware for details.
*/
security?: SecurityHeadersOptions | true;
}StaticApplicationOptions
When static is set, the same HTTP server serves API routes (and /docs, /metrics, WebSocket) as usual; any request that does not match those routes is served from a filesystem directory.
interface StaticApplicationOptions {
/** Filesystem path to the directory to serve (static root). Absolute or relative to cwd. */
root: string;
/**
* URL path prefix under which static files are served.
* Omit or '/' = serve static for all paths not matched by API.
* Example: '/app' = only paths starting with /app are served (prefix stripped when resolving file).
*/
pathPrefix?: string;
/**
* Fallback file name (e.g. 'index.html') for SPA-style client-side routing.
* When the requested file is not found, this file under static root is returned.
*/
fallbackFile?: string;
/**
* TTL in ms for caching file existence checks. Use 0 to disable. Default: 60000.
* Uses @onebun/cache CacheService when available, otherwise in-memory cache.
*/
fileExistenceCacheTtlMs?: number;
}Example: SPA on same host
const app = new OneBunApplication(AppModule, {
static: {
root: './dist',
fallbackFile: 'index.html',
},
});
await app.start();
// GET /api/*, /docs, /metrics, /ws handled by framework; GET /, /dashboard, etc. serve dist/ or index.htmlExample: static under a path prefix
const app = new OneBunApplication(AppModule, {
static: {
root: './public',
pathPrefix: '/assets',
},
});
// Only GET /assets/* are served from ./public; e.g. /assets/logo.png -> public/logo.pngMethods
class OneBunApplication {
/** Start the HTTP server */
async start(): Promise<void>;
/** Stop the HTTP server with optional cleanup options */
async stop(options?: {
closeSharedRedis?: boolean;
signal?: string; // e.g., 'SIGTERM', 'SIGINT'
}): Promise<void>;
/** Enable graceful shutdown signal handlers (SIGTERM, SIGINT) */
enableGracefulShutdown(): void;
/** Get configuration service with full type inference via module augmentation */
getConfig(): IConfig<OneBunAppConfig>;
/** Get configuration value by path with full type inference via module augmentation */
getConfigValue<P extends DeepPaths<OneBunAppConfig>>(path: P): DeepValue<OneBunAppConfig, P>;
getConfigValue<T = unknown>(path: string): T;
/** Get logger instance */
getLogger(context?: Record<string, unknown>): SyncLogger;
/** Get HTTP URL where application is listening */
getHttpUrl(): string;
/** Get root module layer */
getLayer(): Layer.Layer<never, never, unknown>;
/** Get a service instance by class from the module container */
getService<T>(serviceClass: new (...args: unknown[]) => T): T;
}Usage Example
import { OneBunApplication } from '@onebun/core';
import { AppModule } from './app.module';
import { envSchema } from './config';
const app = new OneBunApplication(AppModule, {
port: 3000,
basePath: '/api/v1',
envSchema,
metrics: {
enabled: true,
path: '/metrics',
prefix: 'myapp_',
},
tracing: {
enabled: true,
serviceName: 'my-service',
samplingRate: 1.0,
},
});
await app.start();
// Access configuration - fully typed with module augmentation
const port = app.getConfigValue('server.port'); // number (auto-inferred)
const config = app.getConfig();
const host = config.get('server.host'); // string (auto-inferred)
// Get logger
const logger = app.getLogger({ component: 'main' });
logger.info('Application started', { port, host });
// Application will automatically handle shutdown signals (SIGTERM, SIGINT)
// Or stop manually:
await app.stop();Accessing Services Outside of Requests
Use getService() to access services outside of the request context, for example in background tasks or scripts:
const app = new OneBunApplication(AppModule, options);
await app.start();
// Get a service instance
const userService = app.getService(UserService);
// Use the service
await userService.performBackgroundTask();
await userService.sendScheduledEmails();Graceful Shutdown
OneBun enables graceful shutdown by default. When the application receives SIGTERM or SIGINT signals, it automatically:
- Calls
beforeApplicationDestroy(signal)hooks on all services and controllers - Stops the HTTP server
- Closes all WebSocket connections
- Calls
onModuleDestroy()hooks on all services and controllers - Disconnects shared Redis connection
- Calls
onApplicationDestroy(signal)hooks on all services and controllers
// Default: graceful shutdown is enabled
const app = new OneBunApplication(AppModule);
await app.start();
// SIGTERM/SIGINT handlers are automatically registered
// To disable automatic shutdown handling:
const app = new OneBunApplication(AppModule, {
gracefulShutdown: false,
});
await app.start();
app.enableGracefulShutdown(); // Enable manually later if needed
// Programmatic shutdown
await app.stop(); // Closes server, WebSocket, and shared Redis
// Keep shared Redis open for other consumers
await app.stop({ closeSharedRedis: false });
// Pass signal for lifecycle hooks
await app.stop({ signal: 'SIGTERM' });Lifecycle Hooks
Services and controllers can implement lifecycle hooks to execute code at specific points:
| Interface | Method | When Called |
|---|---|---|
OnModuleInit | onModuleInit() | After instantiation and DI |
OnApplicationInit | onApplicationInit() | After all modules, before HTTP server |
OnModuleDestroy | onModuleDestroy() | During shutdown, after HTTP server stops |
BeforeApplicationDestroy | beforeApplicationDestroy(signal?) | Start of shutdown |
OnApplicationDestroy | onApplicationDestroy(signal?) | End of shutdown |
See Services API for detailed usage examples.
MultiServiceApplication
Run multiple services in a single process.
Constructor
new MultiServiceApplication(options: MultiServiceApplicationOptions)MultiServiceApplicationOptions
interface MultiServiceApplicationOptions {
services: ServicesMap;
envSchema?: TypedEnvSchema;
envOptions?: EnvLoadOptions;
metrics?: MetricsOptions;
tracing?: TracingOptions;
queue?: QueueApplicationOptions; // applied to all services
enabledServices?: string[];
excludedServices?: string[];
externalServiceUrls?: Record<string, string>;
}
interface ServiceConfig {
module: Function;
port: number;
host?: string;
basePath?: string;
routePrefix?: string;
envOverrides?: EnvOverrides;
}
type ServicesMap = Record<string, ServiceConfig>;Usage Example
import { MultiServiceApplication } from '@onebun/core';
import { UsersModule } from './users/users.module';
import { OrdersModule } from './orders/orders.module';
import { envSchema } from './config';
const multiApp = new MultiServiceApplication({
services: {
users: {
module: UsersModule,
port: 3001,
routePrefix: true, // Uses 'users' as route prefix
},
orders: {
module: OrdersModule,
port: 3002,
routePrefix: true, // Uses 'orders' as route prefix
envOverrides: {
DB_NAME: { value: 'orders_db' },
},
},
},
envSchema,
enabledServices: ['users', 'orders'],
});
await multiApp.start();
console.log('Running services:', multiApp.getRunningServices());
// ['users', 'orders']
// Stop all services
multiApp.stop();OneBunModule
Internal module class (usually not used directly).
class OneBunModule implements Module {
static create(
moduleClass: Function,
loggerLayer?: Layer.Layer<never, never, unknown>,
config?: unknown,
): Module;
setup(): Effect.Effect<unknown, never, void>;
getControllers(): Function[];
getControllerInstance(controllerClass: Function): Controller | undefined;
getServiceInstance<T>(tag: Context.Tag<T, T>): T | undefined;
getLayer(): Layer.Layer<never, never, unknown>;
getExportedServices(): Map<Context.Tag<unknown, unknown>, unknown>;
}Global Modules
Modules decorated with @Global() automatically make their exported services available in all other modules without explicit import. This is useful for cross-cutting concerns like database connections.
import { Module, Global, Service, BaseService } from '@onebun/core';
@Service()
export class DatabaseService extends BaseService {
async query(sql: string) { /* ... */ }
}
// Mark module as global
@Global()
@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}
// Root module imports DatabaseModule once
@Module({
imports: [DatabaseModule],
})
export class AppModule {}
// All other modules can inject DatabaseService without importing DatabaseModule
@Module({
providers: [UserService], // UserService can inject DatabaseService automatically
})
export class UserModule {}Global Module Utilities:
// Check if module is global
import { isGlobalModule } from '@onebun/core';
isGlobalModule(DatabaseModule); // true
// Clear global registries (for testing)
import { clearGlobalServicesRegistry } from '@onebun/core';
clearGlobalServicesRegistry();Metrics Options
interface MetricsOptions {
/** Enable/disable metrics (default: true) */
enabled?: boolean;
/** HTTP path for metrics endpoint (default: '/metrics') */
path?: string;
/** Default labels for all metrics */
defaultLabels?: Record<string, string>;
/** Enable automatic HTTP metrics (default: true) */
collectHttpMetrics?: boolean;
/** Enable automatic system metrics (default: true) */
collectSystemMetrics?: boolean;
/** Enable GC metrics (default: true) */
collectGcMetrics?: boolean;
/** System metrics collection interval in ms (default: 5000) */
systemMetricsInterval?: number;
/** Custom prefix for all metrics (default: 'onebun_') */
prefix?: string;
/** Buckets for HTTP duration histogram */
httpDurationBuckets?: number[];
}Tracing Options
interface TracingOptions {
/** Enable/disable tracing (default: true) */
enabled?: boolean;
/** Service name (default: 'onebun-service') */
serviceName?: string;
/** Service version (default: '1.0.0') */
serviceVersion?: string;
/** Sampling rate 0.0-1.0 (default: 1.0) */
samplingRate?: number;
/** Trace HTTP requests (default: true) */
traceHttpRequests?: boolean;
/** Trace database queries (default: true) */
traceDatabaseQueries?: boolean;
/** Default span attributes */
defaultAttributes?: Record<string, string | number | boolean>;
/** Export options for external tracing systems */
exportOptions?: {
endpoint?: string;
headers?: Record<string, string>;
timeout?: number;
batchSize?: number;
batchTimeout?: number;
};
}Re-exports
The core package re-exports commonly used items:
// From @onebun/envs
export { Env, type EnvSchema, EnvValidationError } from '@onebun/envs';
// From @onebun/logger
export type { SyncLogger } from '@onebun/logger';
// From @onebun/requests
export {
createHttpClient,
type ErrorResponse,
HttpStatusCode,
InternalServerError,
isErrorResponse,
NotFoundError,
OneBunBaseError,
type SuccessResponse,
} from '@onebun/requests';
// From @onebun/trace
export { Span } from '@onebun/trace';
// From effect
export { Effect, Layer } from 'effect';
// Internal
export { OneBunApplication } from './application';
export { Controller as BaseController } from './controller';
export { BaseService, Service, getServiceTag } from './service';
export { OneBunModule } from './module';
export * from './decorators';
export * from './validation';