Lifecycle Hooks
OzeanJs provides a Lifecycle Hooks system, a powerful mechanism to "hook" into key moments of the application's lifecycle. This feature allows you to manage resources (like database connections) systematically and safely.
What Are Lifecycle Hooks?
Lifecycle Hooks are special methods that you can add to your Provider or Controller classes. OzeanJs will automatically call these methods at specific, predetermined moments.
Key Hooks:
onModuleInit()
: Called after a module and all of its dependencies have been created and resolved. Ideal for logic that needs to run once everything in the module is ready.onApplicationBootstrap()
: Called after the entire application has finished bootstrapping and is ready to receive requests. Ideal for one-time setup tasks at the very beginning.onApplicationShutdown()
: Called when the application receives a shutdown signal (e.g., fromCtrl+C
). This is the perfect place for closing connections or cleaning up resources (Graceful Shutdown).
Using onModuleInit
and onApplicationBootstrap
Let's look at an example of creating a DatabaseService
that establishes a connection and a SeederService
that populates initial data.
Step 1: Create DatabaseService
with onModuleInit
We use onModuleInit
to ensure the database connection is established only after all dependencies (like a ConfigService
) are available.
// src/database/database.service.ts
import { Injectable, OnModuleInit } from 'ozean';
@Injectable()
export class DatabaseService implements OnModuleInit {
private isConnected = false;
// The constructor runs first
constructor() {
console.log('[DatabaseService] constructor: Service is being created.');
}
// onModuleInit is called after the constructor and all DI is complete
onModuleInit() {
console.log('[DatabaseService] onModuleInit: Module is ready. Connecting to database...');
// Place your database connection logic here
this.isConnected = true;
console.log('[DatabaseService] Database connection established.');
}
// A method for other services to use
async seedUsers(users: any[]) {
if (!this.isConnected) throw new Error('Database not connected.');
console.log('[DatabaseService] Seeding users data...');
// ... logic to insert data ...
return { success: true, count: users.length };
}
}
Step 2: Create SeederService
with onApplicationBootstrap
We use onApplicationBootstrap
to run the data seeding logic only once after the entire application has started up completely.
// src/database/seeder.service.ts
import { Injectable, OnApplicationBootstrap } from 'ozean';
import { DatabaseService } from './database.service';
@Injectable()
export class SeederService implements OnApplicationBootstrap {
// Inject the DatabaseService
constructor(private readonly databaseService: DatabaseService) {}
// This hook is called after onModuleInit has completed for all modules
async onApplicationBootstrap() {
console.log('[SeederService] onApplicationBootstrap: App is ready. Seeding initial data...');
const initialUsers = [{ name: 'Admin User' }, { name: 'Editor User' }];
await this.databaseService.seedUsers(initialUsers);
console.log('[SeederService] Data seeding complete.');
}
}
Using OnApplicationShutdown
(Graceful Shutdown)
This is the most common and important example: using a hook to properly close database connections when the application shuts down.
Step 1: Implement OnApplicationShutdown
in a Service
We will add OnApplicationShutdown
to our DatabaseService
.
// src/database/database.service.ts (Complete version)
import { Injectable, OnModuleInit, OnApplicationShutdown } from 'ozean';
@Injectable()
export class DatabaseService implements OnModuleInit, OnApplicationShutdown {
private isConnected = false;
constructor() {
console.log('[DatabaseService] constructor called.');
}
onModuleInit() {
console.log('[DatabaseService] onModuleInit: Connecting to database...');
this.isConnected = true;
}
// This hook will be called automatically when the app shuts down
async onApplicationShutdown(signal?: string) {
console.log(
`[DatabaseService] onApplicationShutdown: Received signal (${signal}). Closing connections...`
);
// Place your connection pool closing logic here
// await this.pool.end();
this.isConnected = false;
console.log('[DatabaseService] Connections closed gracefully.');
}
// ... other methods ...
}
Step 2: Register and Enable
Finally, don't forget to register all services in a Module and enable the Shutdown Hooks in your main.ts
file.
// src/app.module.ts
@Module({
providers: [DatabaseService, SeederService], // <-- Register both services
})
export class AppModule {}
// src/main.ts
async function bootstrap() {
const app = new App(AppModule);
// Enable Graceful Shutdown
app.enableShutdownHooks();
app.listen(3000);
}
bootstrap();
Using Lifecycle Hooks makes your application more robust and allows you to manage resources correctly and safely throughout all stages of its life.