Files
boilerplate-be/src/common/base/base.service.ts
Fahri Can Seçer 6ef44f398d
Some checks failed
CI / build (push) Failing after 1m58s
main
2026-01-26 23:22:38 +03:00

166 lines
3.5 KiB
TypeScript

import { NotFoundException, Logger } from '@nestjs/common';
import { PrismaService } from '../../database/prisma.service';
import { PaginationDto } from '../dto/pagination.dto';
import { PaginationMeta } from '../types/api-response.type';
/**
* Generic base service with common CRUD operations
* Extend this class for entity-specific services
*/
export abstract class BaseService<T, CreateDto, UpdateDto> {
protected readonly logger: Logger;
constructor(
protected readonly prisma: PrismaService,
protected readonly modelName: string,
) {
this.logger = new Logger(`${modelName}Service`);
}
/**
* Get the Prisma model delegate
*/
protected get model() {
return (this.prisma as any)[this.modelName.toLowerCase()];
}
/**
* Find all records with pagination
*/
async findAll(
pagination: PaginationDto,
where?: any,
): Promise<{ items: T[]; meta: PaginationMeta }> {
const { skip, take, orderBy } = pagination;
const [items, total] = await Promise.all([
this.model.findMany({
where,
skip,
take,
orderBy,
}),
this.model.count({ where }),
]);
const totalPages = Math.ceil(total / take);
return {
items,
meta: {
total,
page: pagination.page || 1,
limit: pagination.limit || 10,
totalPages,
hasNextPage: (pagination.page || 1) < totalPages,
hasPreviousPage: (pagination.page || 1) > 1,
},
};
}
/**
* Find a single record by ID
*/
async findOne(id: string, include?: any): Promise<T> {
const record = await this.model.findUnique({
where: { id },
include,
});
if (!record) {
throw new NotFoundException(`${this.modelName} not found`);
}
return record;
}
/**
* Find a single record by custom criteria
*/
findOneBy(where: any, include?: any): Promise<T | null> {
return this.model.findFirst({
where,
include,
});
}
/**
* Create a new record
*/
create(data: CreateDto, include?: any): Promise<T> {
return this.model.create({
data,
include,
});
}
/**
* Update an existing record
*/
async update(id: string, data: UpdateDto, include?: any): Promise<T> {
// Check if record exists
await this.findOne(id);
return this.model.update({
where: { id },
data,
include,
});
}
/**
* Soft delete a record (sets deletedAt)
*/
async delete(id: string): Promise<T> {
// Check if record exists
await this.findOne(id);
return this.model.delete({
where: { id },
});
}
/**
* Hard delete a record (permanently removes)
*/
async hardDelete(id: string): Promise<T> {
// Check if record exists
await this.findOne(id);
return this.prisma.hardDelete(this.modelName, { id });
}
/**
* Restore a soft-deleted record
*/
async restore(id: string): Promise<T> {
return this.prisma.restore(this.modelName, { id });
}
/**
* Check if a record exists
*/
async exists(id: string): Promise<boolean> {
const count = await this.model.count({
where: { id },
});
return count > 0;
}
/**
* Count records matching criteria
*/
count(where?: any): Promise<number> {
return this.model.count({ where });
}
/**
* Execute a transaction
*/
transaction<R>(fn: (prisma: PrismaService) => Promise<R>): Promise<R> {
return this.prisma.$transaction(async (tx) => {
return fn(tx as unknown as PrismaService);
});
}
}