Define and implement core constructs such as Application and Component for LoopBack 4 framework
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
LoopBack Core provides comprehensive lifecycle management for coordinating application startup and shutdown sequences. The system ensures that all components, servers, and services are initialized, started, and stopped in the correct order with proper dependency management.
Core interface for implementing lifecycle-aware components that need to participate in application startup and shutdown sequences.
/**
* Observers to handle life cycle init/start/stop events
*/
interface LifeCycleObserver {
/** The method to be invoked during init. Called at most once per application instance. */
init?(...injectedArgs: unknown[]): ValueOrPromise<void>;
/** The method to be invoked during start */
start?(...injectedArgs: unknown[]): ValueOrPromise<void>;
/** The method to be invoked during stop */
stop?(...injectedArgs: unknown[]): ValueOrPromise<void>;
}Usage Examples:
import { LifeCycleObserver, inject } from "@loopback/core";
class DatabaseObserver implements LifeCycleObserver {
constructor(
@inject('datasources.db') private datasource: DataSource,
@inject('config.database') private config: DatabaseConfig
) {}
async init(): Promise<void> {
console.log('Initializing database connection...');
await this.datasource.connect();
}
async start(): Promise<void> {
console.log('Database connection ready');
await this.runMigrations();
}
async stop(): Promise<void> {
console.log('Closing database connection...');
await this.datasource.disconnect();
}
private async runMigrations(): Promise<void> {
if (this.config.autoMigrate) {
// Run database migrations
}
}
}Decorator for marking classes as lifecycle observers with optional group assignment and binding specifications.
/**
* Sugar decorator to mark a class as life cycle observer
*/
function lifeCycleObserver(group?: string, ...specs: BindingSpec[]): ClassDecorator;Usage Examples:
import { lifeCycleObserver, LifeCycleObserver, BindingScope } from "@loopback/core";
// Basic lifecycle observer
@lifeCycleObserver()
class CacheObserver implements LifeCycleObserver {
async start(): Promise<void> {
console.log('Cache warming up...');
}
async stop(): Promise<void> {
console.log('Cache shutting down...');
}
}
// Observer with group assignment
@lifeCycleObserver('database')
class DatabaseObserver implements LifeCycleObserver {
async init(): Promise<void> {
console.log('Database initializing...');
}
}
// Observer with binding specifications
@lifeCycleObserver('server', {scope: BindingScope.SINGLETON})
class ServerObserver implements LifeCycleObserver {
async start(): Promise<void> {
console.log('Server starting...');
}
}
// Observer with multiple specifications
@lifeCycleObserver('external-services',
{scope: BindingScope.SINGLETON},
{tags: {priority: 'high'}}
)
class ExternalServiceObserver implements LifeCycleObserver {
async init(): Promise<void> {
console.log('Connecting to external services...');
}
}Helper functions for working with lifecycle observers and bindings.
/**
* Test if an object implements LifeCycleObserver
*/
function isLifeCycleObserver(obj: object): obj is LifeCycleObserver;
/**
* Test if a class implements LifeCycleObserver
*/
function isLifeCycleObserverClass(
ctor: Constructor<unknown>
): ctor is Constructor<LifeCycleObserver>;
/**
* A BindingTemplate function to configure the binding as life cycle observer
* by tagging it with CoreTags.LIFE_CYCLE_OBSERVER.
*/
function asLifeCycleObserver<T = unknown>(binding: Binding<T>): Binding<T>;
/**
* Find all life cycle observer bindings. By default, a binding tagged with
* CoreTags.LIFE_CYCLE_OBSERVER. Used as BindingFilter.
*/
const lifeCycleObserverFilter: BindingTagFilter;Usage Examples:
import {
isLifeCycleObserver,
isLifeCycleObserverClass,
asLifeCycleObserver,
lifeCycleObserverFilter,
LifeCycleObserver
} from "@loopback/core";
// Check if object implements lifecycle observer
const service = new MyService();
if (isLifeCycleObserver(service)) {
await service.start?.();
}
// Check if class implements lifecycle observer
class MyObserver implements LifeCycleObserver {
async start(): Promise<void> {}
}
if (isLifeCycleObserverClass(MyObserver)) {
console.log('MyObserver implements LifeCycleObserver');
}
// Configure binding as lifecycle observer
const binding = context.bind('my-observer')
.toClass(MyObserver)
.apply(asLifeCycleObserver);
// Find all lifecycle observer bindings
const observerBindings = context.find(lifeCycleObserverFilter);Central registry that manages and coordinates lifecycle observers with support for grouping, ordering, and parallel execution.
/**
* A context-based registry for life cycle observers
*/
class LifeCycleObserverRegistry implements LifeCycleObserver {
constructor(
context: Context,
observersView: ContextView<LifeCycleObserver>,
options?: LifeCycleObserverOptions
);
/** Set the ordered groups for observer execution */
setOrderedGroups(groups: string[]): void;
/** Get observer groups ordered by the group configuration */
getObserverGroupsByOrder(): LifeCycleObserverGroup[];
/** Notify all life cycle observers by group of init */
init(): Promise<void>;
/** Notify all life cycle observers by group of start */
start(): Promise<void>;
/** Notify all life cycle observers by group of stop */
stop(): Promise<void>;
}Usage Examples:
import {
LifeCycleObserverRegistry,
LifeCycleObserverOptions,
DEFAULT_ORDERED_GROUPS
} from "@loopback/core";
// Custom registry configuration
const options: LifeCycleObserverOptions = {
orderedGroups: ['database', 'cache', 'server'],
disabledGroups: ['optional-services'],
parallel: true
};
// Manual registry usage (typically handled by Application)
const registry = new LifeCycleObserverRegistry(
context,
observersView,
options
);
// Set custom ordering
registry.setOrderedGroups(['datasource', 'server', 'monitoring']);
// Get ordered groups
const groups = registry.getObserverGroupsByOrder();
console.log('Observer groups:', groups.map(g => g.group));
// Manual lifecycle management
await registry.init();
await registry.start();
await registry.stop();Configuration interface for controlling how lifecycle observers are executed.
interface LifeCycleObserverOptions {
/** Control the order of observer groups for notifications */
orderedGroups: string[];
/** Override and disable lifecycle observer groups */
disabledGroups?: string[];
/** Notify observers of the same group in parallel, default to true */
parallel?: boolean;
}
/** Default ordered groups configuration */
const DEFAULT_ORDERED_GROUPS: string[];Usage Examples:
import {
LifeCycleObserverOptions,
DEFAULT_ORDERED_GROUPS,
Application
} from "@loopback/core";
// Application with custom lifecycle options
const app = new Application();
// Configure lifecycle observer options
app.bind('lifeCycleObserver.options').to({
orderedGroups: ['database', 'cache', 'messaging', 'server'],
disabledGroups: ['debug-tools'],
parallel: true
} as LifeCycleObserverOptions);
// Use default groups (typically just ['server'])
console.log('Default groups:', DEFAULT_ORDERED_GROUPS);
// Sequential execution for critical services
const criticalOptions: LifeCycleObserverOptions = {
orderedGroups: ['database', 'auth', 'server'],
parallel: false // Execute sequentially within groups
};Type definition for organizing lifecycle observers into logical groups with coordinated execution.
/**
* A group of life cycle observers
*/
interface LifeCycleObserverGroup {
/** Observer group name */
group: string;
/** Bindings for observers within the group */
bindings: Readonly<Binding<LifeCycleObserver>>[];
}Usage Examples:
import { LifeCycleObserverGroup, lifeCycleObserver } from "@loopback/core";
// Observers in different groups
@lifeCycleObserver('database')
class DatabaseConnectionObserver implements LifeCycleObserver {
async init(): Promise<void> {
console.log('Database connection initializing...');
}
}
@lifeCycleObserver('database')
class DatabaseMigrationObserver implements LifeCycleObserver {
async start(): Promise<void> {
console.log('Running database migrations...');
}
}
@lifeCycleObserver('server')
class HttpServerObserver implements LifeCycleObserver {
async start(): Promise<void> {
console.log('HTTP server starting...');
}
}
// Groups will be organized as:
// {
// group: 'database',
// bindings: [DatabaseConnectionObserver, DatabaseMigrationObserver]
// }
// {
// group: 'server',
// bindings: [HttpServerObserver]
// }How lifecycle management integrates with the main Application class.
Usage Examples:
import { Application, lifeCycleObserver, LifeCycleObserver } from "@loopback/core";
@lifeCycleObserver('cache')
class RedisObserver implements LifeCycleObserver {
async init(): Promise<void> {
console.log('Connecting to Redis...');
}
async start(): Promise<void> {
console.log('Redis cache ready');
}
async stop(): Promise<void> {
console.log('Disconnecting from Redis...');
}
}
@lifeCycleObserver('server')
class WebServerObserver implements LifeCycleObserver {
async start(): Promise<void> {
console.log('Web server listening...');
}
async stop(): Promise<void> {
console.log('Web server shut down');
}
}
const app = new Application();
// Register lifecycle observers
app.lifeCycleObserver(RedisObserver);
app.lifeCycleObserver(WebServerObserver);
// Application coordinates all observers
await app.start();
// Output:
// Connecting to Redis...
// Redis cache ready
// Web server listening...
await app.stop();
// Output:
// Web server shut down
// Disconnecting from Redis...type ValueOrPromise<T> = T | Promise<T>;
interface LifeCycleObserverGroup {
group: string;
bindings: Readonly<Binding<LifeCycleObserver>>[];
}
interface LifeCycleObserverOptions {
orderedGroups: string[];
disabledGroups?: string[];
parallel?: boolean;
}
const DEFAULT_ORDERED_GROUPS: string[];