# How to Implement NestJS Lifecycle Hooks: OnModuleInit and OnModuleDestroy

> Learn to implement NestJS lifecycle hooks OnModuleInit and OnModuleDestroy. Execute custom logic on module initialization and before application shutdown.

- Repository: [nestjs/nest](https://github.com/nestjs/nest)
- Tags: how-to-guide
- Published: 2026-03-01

---

**Implement the `OnModuleInit` and `OnModuleDestroy` interfaces in your providers, controllers, or modules to execute custom logic immediately after dependency resolution and immediately before application shutdown.**

NestJS provides a built-in lifecycle hook system that lets you run initialization code after all dependencies are resolved and cleanup code when the application begins shutting down. By implementing these interfaces from `@nestjs/common`, you can manage database connections, seed data, or gracefully close external resources. This guide demonstrates how to implement lifecycle hooks using the actual source code from the `nestjs/nest` repository.

## Understanding the Lifecycle Hook Interfaces

The lifecycle contracts are defined as simple TypeScript interfaces in the common package. Each interface exposes exactly one method that NestJS calls automatically during the application lifecycle.

In [`packages/common/interfaces/hooks/on-init.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/hooks/on-init.interface.ts), the `OnModuleInit` interface is defined as:

```typescript
export interface OnModuleInit {
  onModuleInit(): any;
}

```

Similarly, [`packages/common/interfaces/hooks/on-destroy.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/hooks/on-destroy.interface.ts) defines `OnModuleDestroy`:

```typescript
export interface OnModuleDestroy {
  onModuleDestroy(): any;
}

```

The return type `any` allows you to return a Promise for asynchronous operations. NestJS detects and awaits promises automatically, ensuring your async initialization completes before the application accepts requests.

## How NestJS Executes Lifecycle Hooks

When the application bootstraps, the core module resolver iterates over every provider, controller, and middleware to locate classes implementing these interfaces.

The `callModuleInitHook` function in [`packages/core/hooks/on-module-init.hook.ts`](https://github.com/nestjs/nest/blob/main/packages/core/hooks/on-module-init.hook.ts) performs the following steps:

1. Collects all non-transient and transient instances from the module
2. Filters instances that implement `OnModuleInit`
3. Invokes `onModuleInit()` on each instance
4. Finally calls the hook on the module class itself if it implements the interface

The symmetric `callModuleDestroyHook` in [`packages/core/hooks/on-module-destroy.hook.ts`](https://github.com/nestjs/nest/blob/main/packages/core/hooks/on-module-destroy.hook.ts) executes during shutdown, running `onModuleDestroy()` on every eligible instance before the application process exits.

Both hooks respect **static dependency trees** (verified via `moduleClassHost.isDependencyTreeStatic()`) to ensure they execute exactly once per module, avoiding duplicate calls for dynamically scoped providers.

## When to Use Each Hook

| Hook | Execution Timing | Common Use Cases |
|------|-----------------|------------------|
| **OnModuleInit** | After all providers, controllers, and middlewares of a module have been instantiated and their dependencies resolved. | Initialize database clients, connect to external APIs, seed reference data, warm up caches. |
| **OnModuleDestroy** | Immediately before `app.close()` completes, during the application shutdown phase. | Close database connections, stop background timers, flush pending writes, unsubscribe from message brokers. |

## Implementing OnModuleInit in Providers

Providers are the most common place to implement lifecycle hooks. The following example from the NestJS samples shows a Prisma service that establishes its database connection automatically when the module initializes:

```typescript
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
    console.log('Prisma connected');
  }
}

```

*Source: [`sample/22-graphql-prisma/src/prisma/prisma.service.ts`](https://github.com/nestjs/nest/blob/main/sample/22-graphql-prisma/src/prisma/prisma.service.ts)*

By extending `PrismaClient` and implementing `OnModuleInit`, the service connects to the database immediately after NestJS instantiates it and resolves any injected dependencies.

## Implementing OnModuleDestroy for Cleanup

For resources that require explicit teardown, implement `OnModuleDestroy` to prevent memory leaks and hanging connections. This Redis client example demonstrates graceful shutdown:

```typescript
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import * as redis from 'redis';

@Injectable()
export class CacheService implements OnModuleInit, OnModuleDestroy {
  private client: redis.RedisClient;

  onModuleInit() {
    this.client = redis.createClient();
    this.client.on('ready', () => console.log('Redis ready'));
  }

  onModuleDestroy() {
    this.client.quit();
    console.log('Redis connection closed');
  }
}

```

The `onModuleDestroy()` method ensures the Redis client closes its connection properly when you call `app.close()` or the process receives a shutdown signal.

## Adding Hooks Directly to Module Classes

You can also implement lifecycle hooks directly in the module class itself. This is useful for module-level initialization logic that coordinates multiple providers:

```typescript
import { Module, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { CacheService } from './cache.service';

@Module({
  providers: [CacheService],
})
export class AppModule implements OnModuleInit, OnModuleDestroy {
  onModuleInit() {
    console.log('Application initialized');
  }

  onModuleDestroy() {
    console.log('Application shutting down');
  }
}

```

NestJS detects that `AppModule` implements these interfaces and invokes the hooks at the appropriate times, just as it does for providers.

## Using Hooks in Controllers

While less common, controllers can also implement lifecycle hooks. The following pattern tracks application uptime by recording the initialization timestamp:

```typescript
import { Controller, Get, OnModuleInit } from '@nestjs/common';

@Controller('status')
export class StatusController implements OnModuleInit {
  private startTime: number;

  onModuleInit() {
    this.startTime = Date.now();
  }

  @Get()
  health() {
    return { uptime: Date.now() - this.startTime };
  }
}

```

*Source: [`sample/04-grpc/src/hero/hero.controller.ts`](https://github.com/nestjs/nest/blob/main/sample/04-grpc/src/hero/hero.controller.ts)*

## Summary

- **Implement `OnModuleInit`** in [`packages/common/interfaces/hooks/on-init.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/hooks/on-init.interface.ts) to run code after all module dependencies resolve.
- **Implement `OnModuleDestroy`** in [`packages/common/interfaces/hooks/on-destroy.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/hooks/on-destroy.interface.ts) to clean up resources before the application exits.
- **Execution is automatic** via `callModuleInitHook` and `callModuleDestroyHook` in the core hooks directory.
- **Return type is `any`**, supporting both synchronous and asynchronous (Promise-based) implementations.
- **Apply hooks to providers, controllers, or modules** depending on which scope owns the resource lifecycle.

## Frequently Asked Questions

### Can lifecycle hooks return Promises for async operations?

Yes. The interfaces define the return type as `any`, which includes Promises. The core execution logic in [`on-module-init.hook.ts`](https://github.com/nestjs/nest/blob/main/on-module-init.hook.ts) and [`on-module-destroy.hook.ts`](https://github.com/nestjs/nest/blob/main/on-module-destroy.hook.ts) properly awaits any returned promises, ensuring your asynchronous initialization or cleanup completes before the application continues booting or fully terminates.

### Does OnModuleDestroy run when the process crashes or only on graceful shutdown?

`OnModuleDestroy` only executes during graceful shutdown when you explicitly call `app.close()` or when NestJS catches termination signals (SIGTERM, SIGINT) and triggers its shutdown hooks. It will not run on unhandled exceptions or process crashes that bypass the NestJS shutdown sequence.

### Can a single class implement both OnModuleInit and OnModuleDestroy?

Yes. A provider, controller, or module can implement both interfaces simultaneously to handle both initialization and cleanup logic in one location. The NestJS lifecycle system will invoke `onModuleInit()` during startup and `onModuleDestroy()` during shutdown for the same instance.

### Do lifecycle hooks work with transient-scoped providers?

Yes. The `callModuleInitHook` and `callModuleDestroyHook` functions explicitly collect both non-transient and transient instances. However, hooks respect static dependency trees to avoid duplicate execution, ensuring each unique instance receives the hook call exactly once during its lifecycle.