9
src/database/database.module.ts
Normal file
9
src/database/database.module.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { PrismaService } from './prisma.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [PrismaService],
|
||||
exports: [PrismaService],
|
||||
})
|
||||
export class DatabaseModule {}
|
||||
134
src/database/prisma.service.ts
Normal file
134
src/database/prisma.service.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
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<string, unknown> }) => Promise<unknown>;
|
||||
findMany: (args?: Record<string, unknown>) => Promise<unknown[]>;
|
||||
update: (args: {
|
||||
where: Record<string, unknown>;
|
||||
data: Record<string, unknown>;
|
||||
}) => Promise<unknown>;
|
||||
}
|
||||
|
||||
@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<T>(model: string, where: Record<string, unknown>): Promise<T> {
|
||||
const delegate = this.getModelDelegate(model);
|
||||
return delegate.delete({ where }) as Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find including soft deleted records
|
||||
*/
|
||||
findWithDeleted<T>(
|
||||
model: string,
|
||||
args?: Record<string, unknown>,
|
||||
): Promise<T[]> {
|
||||
const delegate = this.getModelDelegate(model);
|
||||
return delegate.findMany(args) as Promise<T[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a soft deleted record
|
||||
*/
|
||||
restore<T>(model: string, where: Record<string, unknown>): Promise<T> {
|
||||
const delegate = this.getModelDelegate(model);
|
||||
return delegate.update({
|
||||
where,
|
||||
data: { deletedAt: null },
|
||||
}) as Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete - set deletedAt to current date
|
||||
*/
|
||||
softDelete<T>(model: string, where: Record<string, unknown>): Promise<T> {
|
||||
const delegate = this.getModelDelegate(model);
|
||||
return delegate.update({
|
||||
where,
|
||||
data: { deletedAt: new Date() },
|
||||
}) as Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find many excluding soft deleted records
|
||||
*/
|
||||
findManyActive<T>(
|
||||
model: string,
|
||||
args?: Record<string, unknown>,
|
||||
): Promise<T[]> {
|
||||
const delegate = this.getModelDelegate(model);
|
||||
const whereWithDeleted = {
|
||||
...args,
|
||||
where: {
|
||||
...(args?.where as Record<string, unknown> | undefined),
|
||||
deletedAt: null,
|
||||
},
|
||||
};
|
||||
return delegate.findMany(whereWithDeleted) as Promise<T[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user