New skills (14): - nestjs-best-practices: 40 priority-ranked rules (kadajett) - fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb) - architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson) - python-performance-optimization: Profiling and optimization (wshobson) - ai-sdk: Vercel AI SDK streaming and agent patterns (vercel) - create-agent: Modular agent architecture with OpenRouter (openrouterteam) - proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster) - brand-guidelines: Brand identity enforcement (anthropics) - ui-animation: Motion design with accessibility (mblode) - marketing-ideas: 139 ideas across 14 categories (coreyhaines31) - pricing-strategy: SaaS pricing and tier design (coreyhaines31) - programmatic-seo: SEO at scale with playbooks (coreyhaines31) - competitor-alternatives: Comparison page architecture (coreyhaines31) - referral-program: Referral and affiliate programs (coreyhaines31) README reorganized by domain: Code Quality, Frontend, Backend, Auth, AI/Agent Building, Marketing, Design, Meta. Mosaic Stack is not limited to coding — the Orchestrator serves coding, business, design, marketing, writing, logistics, and analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
110 lines
2.8 KiB
Markdown
110 lines
2.8 KiB
Markdown
---
|
|
title: Use Async Lifecycle Hooks Correctly
|
|
impact: HIGH
|
|
impactDescription: Improper async handling blocks application startup
|
|
tags: performance, lifecycle, async, hooks
|
|
---
|
|
|
|
## Use Async Lifecycle Hooks Correctly
|
|
|
|
NestJS 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.
|
|
|
|
**Incorrect (fire-and-forget async without await):**
|
|
|
|
```typescript
|
|
// Fire-and-forget async without await
|
|
@Injectable()
|
|
export class DatabaseService implements OnModuleInit {
|
|
onModuleInit() {
|
|
// This runs but doesn't block - app starts before DB is ready!
|
|
this.connect();
|
|
}
|
|
|
|
private async connect() {
|
|
await this.pool.connect();
|
|
console.log('Database connected');
|
|
}
|
|
}
|
|
|
|
// Heavy blocking operations in constructor
|
|
@Injectable()
|
|
export class ConfigService {
|
|
private config: Config;
|
|
|
|
constructor() {
|
|
// BLOCKS entire module instantiation synchronously
|
|
this.config = fs.readFileSync('config.json');
|
|
}
|
|
}
|
|
```
|
|
|
|
**Correct (return promises from async hooks):**
|
|
|
|
```typescript
|
|
// Return promise from async hooks
|
|
@Injectable()
|
|
export class DatabaseService implements OnModuleInit {
|
|
private pool: Pool;
|
|
|
|
async onModuleInit(): Promise<void> {
|
|
// NestJS waits for this to complete before continuing
|
|
await this.pool.connect();
|
|
console.log('Database connected');
|
|
}
|
|
|
|
async onModuleDestroy(): Promise<void> {
|
|
// Clean up resources on shutdown
|
|
await this.pool.end();
|
|
console.log('Database disconnected');
|
|
}
|
|
}
|
|
|
|
// Use onApplicationBootstrap for cross-module dependencies
|
|
@Injectable()
|
|
export class CacheWarmerService implements OnApplicationBootstrap {
|
|
constructor(
|
|
private cache: CacheService,
|
|
private products: ProductsService,
|
|
) {}
|
|
|
|
async onApplicationBootstrap(): Promise<void> {
|
|
// All modules are initialized, safe to warm cache
|
|
const products = await this.products.findPopular();
|
|
await this.cache.warmup(products);
|
|
}
|
|
}
|
|
|
|
// Heavy init in async hooks, not constructor
|
|
@Injectable()
|
|
export class ConfigService implements OnModuleInit {
|
|
private config: Config;
|
|
|
|
constructor() {
|
|
// Keep constructor synchronous and fast
|
|
}
|
|
|
|
async onModuleInit(): Promise<void> {
|
|
// Async loading in lifecycle hook
|
|
this.config = await this.loadConfig();
|
|
}
|
|
|
|
private async loadConfig(): Promise<Config> {
|
|
const file = await fs.promises.readFile('config.json');
|
|
return JSON.parse(file.toString());
|
|
}
|
|
|
|
get<T>(key: string): T {
|
|
return this.config[key];
|
|
}
|
|
}
|
|
|
|
// Enable shutdown hooks in main.ts
|
|
async function bootstrap() {
|
|
const app = await NestFactory.create(AppModule);
|
|
app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling
|
|
await app.listen(3000);
|
|
}
|
|
```
|
|
|
|
Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)
|