Introduction
NestJS applications have a defined lifecycle—from initialization to shutdown. Lifecycle hooks let you run code at specific points: when modules initialize, when the app starts listening, or when it's shutting down. This is essential for resource management.
Key Concepts
- OnModuleInit: Called after module dependencies are resolved
- OnApplicationBootstrap: Called after all modules initialized, before listening
- OnModuleDestroy: Called when app receives shutdown signal
- BeforeApplicationShutdown: Called before connections close
- OnApplicationShutdown: Called after all connections are closed, receives shutdown signal
Real World Context
Use lifecycle hooks for:
- Initializing database connections
- Starting background jobs
- Warming caches
- Gracefully closing connections on shutdown
- Cleanup operations
Deep Dive
Lifecycle Hook Order
onModuleInit()- Each provider, then each moduleonApplicationBootstrap()- Same order- App starts listening for connections
onModuleDestroy()- On shutdown signalbeforeApplicationShutdown()- Same order- All connections closed
onApplicationShutdown()- Same order- Process exits
Implementing Hooks
typescriptimport { Injectable, OnModuleInit, OnApplicationBootstrap, OnModuleDestroy } from '@nestjs/common'; @Injectable() export class DatabaseService implements OnModuleInit, OnApplicationBootstrap, OnModuleDestroy, OnApplicationShutdown { async onModuleInit() { console.log('Module dependencies resolved'); await this.connect(); } async onApplicationBootstrap() { console.log('App ready, about to listen'); await this.runMigrations(); } async onModuleDestroy() { console.log('Shutting down...'); await this.disconnect(); } async onApplicationShutdown(signal?: string) { console.log(`App shutdown complete (signal: ${signal})`); } }
Enabling Shutdown Hooks
typescriptasync function bootstrap() { const app = await NestFactory.create(AppModule); // Enable graceful shutdown app.enableShutdownHooks(); await app.listen(3000); }
BeforeApplicationShutdown
typescript@Injectable() export class AppService implements BeforeApplicationShutdown { async beforeApplicationShutdown(signal?: string) { console.log(`Received signal: ${signal}`); // Cleanup before connections close await this.flushLogs(); } }
Common Pitfalls
- Forgetting enableShutdownHooks: Without it, shutdown hooks don't fire on SIGTERM/SIGINT.
- Blocking async operations: Long operations in hooks delay startup/shutdown.
- Not handling errors: Unhandled errors in hooks can crash the app.
Best Practices
- Always enable shutdown hooks in production
- Keep hook operations fast; queue longer work
- Log hook execution for debugging
- Handle errors gracefully in hooks
- Use timeouts for shutdown operations
Summary
Lifecycle hooks run code at specific points in the app lifecycle. Implement OnModuleInit for initialization, OnModuleDestroy for cleanup. Enable shutdown hooks with enableShutdownHooks() for graceful termination.
Code Examples
typescript
@Injectable()
export class DatabaseService
implements OnModuleInit, OnModuleDestroy, OnApplicationShutdown {
async onModuleInit() {
await this.connect();
}
async onModuleDestroy() {
await this.disconnect();
}
async onApplicationShutdown(signal?: string) {
console.log(`Shutdown: ${signal}`);
}
}