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/devops-graceful-shutdown.md
1---2title: Implement Graceful Shutdown3impact: MEDIUM-HIGH4impactDescription: Proper shutdown handling ensures zero-downtime deployments5tags: devops, graceful-shutdown, lifecycle, kubernetes6---78## Implement Graceful Shutdown910Handle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments.1112**Incorrect (ignoring shutdown signals):**1314```typescript15// Ignore shutdown signals16async function bootstrap() {17const app = await NestFactory.create(AppModule);18await app.listen(3000);19// App crashes immediately on SIGTERM20// In-flight requests fail21// Database connections are abruptly closed22}2324// Long-running tasks without cancellation25@Injectable()26export class ProcessingService {27async processLargeFile(file: File): Promise<void> {28// No way to interrupt this during shutdown29for (let i = 0; i < file.chunks.length; i++) {30await this.processChunk(file.chunks[i]);31// May run for minutes, blocking shutdown32}33}34}35```3637**Correct (enable shutdown hooks and handle cleanup):**3839```typescript40// Enable shutdown hooks in main.ts41async function bootstrap() {42const app = await NestFactory.create(AppModule);4344// Enable shutdown hooks45app.enableShutdownHooks();4647// Optional: Add timeout for forced shutdown48const server = await app.listen(3000);49server.setTimeout(30000); // 30 second timeout5051// Handle graceful shutdown52const signals = ['SIGTERM', 'SIGINT'];53signals.forEach((signal) => {54process.on(signal, async () => {55console.log(`Received ${signal}, starting graceful shutdown...`);5657// Stop accepting new connections58server.close(async () => {59console.log('HTTP server closed');60await app.close();61process.exit(0);62});6364// Force exit after timeout65setTimeout(() => {66console.error('Forced shutdown after timeout');67process.exit(1);68}, 30000);69});70});71}7273// Lifecycle hooks for cleanup74@Injectable()75export class DatabaseService implements OnApplicationShutdown {76private readonly connections: Connection[] = [];7778async onApplicationShutdown(signal?: string): Promise<void> {79console.log(`Database service shutting down on ${signal}`);8081// Close all connections gracefully82await Promise.all(83this.connections.map((conn) => conn.close()),84);8586console.log('All database connections closed');87}88}8990// Queue processor with graceful shutdown91@Injectable()92export class QueueService implements OnApplicationShutdown, OnModuleDestroy {93private isShuttingDown = false;9495onModuleDestroy(): void {96this.isShuttingDown = true;97}9899async onApplicationShutdown(): Promise<void> {100// Wait for current jobs to complete101await this.queue.close();102}103104async processJob(job: Job): Promise<void> {105if (this.isShuttingDown) {106throw new Error('Service is shutting down');107}108await this.doWork(job);109}110}111112// WebSocket gateway cleanup113@WebSocketGateway()114export class EventsGateway implements OnApplicationShutdown {115@WebSocketServer()116server: Server;117118async onApplicationShutdown(): Promise<void> {119// Notify all connected clients120this.server.emit('shutdown', { message: 'Server is shutting down' });121122// Close all connections123this.server.disconnectSockets();124}125}126127// Health check integration128@Injectable()129export class ShutdownService {130private isShuttingDown = false;131132startShutdown(): void {133this.isShuttingDown = true;134}135136isShutdown(): boolean {137return this.isShuttingDown;138}139}140141@Controller('health')142export class HealthController {143constructor(private shutdownService: ShutdownService) {}144145@Get('ready')146@HealthCheck()147readiness(): Promise<HealthCheckResult> {148// Return 503 during shutdown - k8s stops sending traffic149if (this.shutdownService.isShutdown()) {150throw new ServiceUnavailableException('Shutting down');151}152153return this.health.check([154() => this.db.pingCheck('database'),155]);156}157}158159// Integrate with shutdown160@Injectable()161export class AppShutdownService implements OnApplicationShutdown {162constructor(private shutdownService: ShutdownService) {}163164async onApplicationShutdown(): Promise<void> {165// Mark as unhealthy first166this.shutdownService.startShutdown();167168// Wait for k8s to update endpoints169await this.sleep(5000);170171// Then proceed with cleanup172}173}174175// Request tracking for in-flight requests176@Injectable()177export class RequestTracker implements NestMiddleware, OnApplicationShutdown {178private activeRequests = 0;179private isShuttingDown = false;180private shutdownPromise: Promise<void> | null = null;181private resolveShutdown: (() => void) | null = null;182183use(req: Request, res: Response, next: NextFunction): void {184if (this.isShuttingDown) {185res.status(503).send('Service Unavailable');186return;187}188189this.activeRequests++;190191res.on('finish', () => {192this.activeRequests--;193if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) {194this.resolveShutdown();195}196});197198next();199}200201async onApplicationShutdown(): Promise<void> {202this.isShuttingDown = true;203204if (this.activeRequests > 0) {205console.log(`Waiting for ${this.activeRequests} requests to complete`);206this.shutdownPromise = new Promise((resolve) => {207this.resolveShutdown = resolve;208});209210// Wait with timeout211await Promise.race([212this.shutdownPromise,213new Promise((resolve) => setTimeout(resolve, 30000)),214]);215}216217console.log('All requests completed');218}219}220```221222Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events)223