import { Injectable, OnModuleInit, OnModuleDestroy, Logger, } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; // Models that support soft delete const SOFT_DELETE_MODELS = ['user', 'role', 'tenant']; // Type for Prisma model delegate with common operations interface PrismaDelegate { delete: (args: { where: Record }) => Promise; findMany: (args?: Record) => Promise; update: (args: { where: Record; data: Record; }) => Promise; } @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(PrismaService.name); constructor() { super({ log: [ { emit: 'event', level: 'query' }, { emit: 'event', level: 'error' }, { emit: 'event', level: 'warn' }, ], }); } async onModuleInit() { this.logger.log( `Connecting to database... URL: ${process.env.DATABASE_URL?.split('@')[1]}`, ); // Mask password try { await this.$connect(); this.logger.log('✅ Database connected successfully'); } catch (error) { this.logger.error( `❌ Database connection failed: ${error.message}`, error.stack, ); throw error; } } async onModuleDestroy() { await this.$disconnect(); this.logger.log('🔌 Database disconnected'); } /** * Check if model has soft delete (deletedAt field) */ hasSoftDelete(model: string | undefined): boolean { return model ? SOFT_DELETE_MODELS.includes(model.toLowerCase()) : false; } /** * Hard delete - actually remove from database */ hardDelete(model: string, where: Record): Promise { const delegate = this.getModelDelegate(model); return delegate.delete({ where }) as Promise; } /** * Find including soft deleted records */ findWithDeleted( model: string, args?: Record, ): Promise { const delegate = this.getModelDelegate(model); return delegate.findMany(args) as Promise; } /** * Restore a soft deleted record */ restore(model: string, where: Record): Promise { const delegate = this.getModelDelegate(model); return delegate.update({ where, data: { deletedAt: null }, }) as Promise; } /** * Soft delete - set deletedAt to current date */ softDelete(model: string, where: Record): Promise { const delegate = this.getModelDelegate(model); return delegate.update({ where, data: { deletedAt: new Date() }, }) as Promise; } /** * Find many excluding soft deleted records */ findManyActive( model: string, args?: Record, ): Promise { const delegate = this.getModelDelegate(model); const whereWithDeleted = { ...args, where: { ...(args?.where as Record | undefined), deletedAt: null, }, }; return delegate.findMany(whereWithDeleted) as Promise; } /** * Get Prisma model delegate by name */ private getModelDelegate(model: string): PrismaDelegate { const modelKey = model.charAt(0).toLowerCase() + model.slice(1); return (this as any)[modelKey] as PrismaDelegate; } }