Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/perf-async-hooks.md
1---2title: Use Async Lifecycle Hooks Correctly3impact: HIGH4impactDescription: Improper async handling blocks application startup5tags: performance, lifecycle, async, hooks6---78## Use Async Lifecycle Hooks Correctly910NestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately.1112**Incorrect (fire-and-forget async without await):**1314```typescript15// Fire-and-forget async without await16@Injectable()17export class DatabaseService implements OnModuleInit {18onModuleInit() {19// This runs but doesn't block - app starts before DB is ready!20this.connect();21}2223private async connect() {24await this.pool.connect();25console.log('Database connected');26}27}2829// Heavy blocking operations in constructor30@Injectable()31export class ConfigService {32private config: Config;3334constructor() {35// BLOCKS entire module instantiation synchronously36this.config = fs.readFileSync('config.json');37}38}39```4041**Correct (return promises from async hooks):**4243```typescript44// Return promise from async hooks45@Injectable()46export class DatabaseService implements OnModuleInit {47private pool: Pool;4849async onModuleInit(): Promise<void> {50// NestJS waits for this to complete before continuing51await this.pool.connect();52console.log('Database connected');53}5455async onModuleDestroy(): Promise<void> {56// Clean up resources on shutdown57await this.pool.end();58console.log('Database disconnected');59}60}6162// Use onApplicationBootstrap for cross-module dependencies63@Injectable()64export class CacheWarmerService implements OnApplicationBootstrap {65constructor(66private cache: CacheService,67private products: ProductsService,68) {}6970async onApplicationBootstrap(): Promise<void> {71// All modules are initialized, safe to warm cache72const products = await this.products.findPopular();73await this.cache.warmup(products);74}75}7677// Heavy init in async hooks, not constructor78@Injectable()79export class ConfigService implements OnModuleInit {80private config: Config;8182constructor() {83// Keep constructor synchronous and fast84}8586async onModuleInit(): Promise<void> {87// Async loading in lifecycle hook88this.config = await this.loadConfig();89}9091private async loadConfig(): Promise<Config> {92const file = await fs.promises.readFile('config.json');93return JSON.parse(file.toString());94}9596get<T>(key: string): T {97return this.config[key];98}99}100101// Enable shutdown hooks in main.ts102async function bootstrap() {103const app = await NestFactory.create(AppModule);104app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling105await app.listen(3000);106}107```108109Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)110