Lifecycle Hooks
Services can implement lifecycle hooks for initialization and cleanup.
onInit Hook
Called after a service instance is created:
typescript
class DatabaseConnection {
private connection: any;
async onInit() {
console.log('Initializing database connection...');
this.connection = await connectToDatabase();
console.log('Database connected!');
}
query(sql: string) {
return this.connection.query(sql);
}
}
services.addScoped<DatabaseConnection>(DatabaseConnectionToken, DatabaseConnection);
const scope = provider.createScope();
const db = await scope.getRequiredService<DatabaseConnection>(DatabaseConnectionToken);
// onInit() is automatically calledonDestroy Hook
Called when a scope is disposed:
typescript
class DatabaseConnection {
private connection: any;
async onInit() {
this.connection = await connectToDatabase();
}
async onDestroy() {
if (this.connection) {
await this.connection.close();
console.log('Database connection closed');
}
}
}
const scope = provider.createScope();
const db = await scope.getRequiredService<DatabaseConnection>(DatabaseConnectionToken);
// ... use database
await scope.dispose(); // onDestroy() is automatically calledBoth Hooks
Services can implement both hooks:
typescript
class CacheService {
private cache = new Map<string, any>();
async onInit() {
console.log('Cache initialized');
// Load cache from disk, etc.
}
async onDestroy() {
console.log('Clearing cache...');
this.cache.clear();
// Save cache to disk, etc.
}
}Hook Execution Order
- Service instance is created
- Dependencies are injected
onInit()is called (if implemented)- Service is ready to use
- When scope is disposed,
onDestroy()is called (if implemented)
Async Hooks
Both hooks can be async:
typescript
class DatabaseConnection {
async onInit() {
await this.connect();
await this.migrate();
}
async onDestroy() {
await this.close();
await this.cleanup();
}
}Error Handling
If onInit() throws, the service creation fails:
typescript
class DatabaseConnection {
async onInit() {
throw new Error('Failed to connect');
}
}
// This will throw the error
const db = await provider.getRequiredService<DatabaseConnection>(DatabaseConnectionToken);If onDestroy() throws, the error is logged but doesn't prevent disposal:
typescript
class Service {
async onDestroy() {
throw new Error('Cleanup failed');
}
}
// Error is logged, but disposal continues
await scope.dispose();Use Cases
onInit
- Database connections
- Cache initialization
- Loading configuration
- Setting up event listeners
onDestroy
- Closing database connections
- Clearing caches
- Removing event listeners
- Saving state
Best Practices
- Keep hooks simple - Avoid complex logic
- Handle errors - Wrap in try-catch if needed
- Make hooks async - For async operations
- Clean up in onDestroy - Prevent resource leaks
- Test hooks - Ensure they work correctly