feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma binaryTargets for openssl 3.0 - nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal cron, http->https redirect - prisma: add origin_goods.delisted migration, sync missing schema (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz - api: CORS wildcard reflection, helmet CORP cross-origin, price backfill in persistProductDetail, categoryIcon ancestor fallback, mediaByColor per-color gallery in public goods detail - admin: /admin base path (vite + router) - import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
---
|
||||
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)
|
||||
Reference in New Issue
Block a user