Building a big application in NestJs require a lot of pieces of code to be repeated, so to make it DRY, i implemented in my business' a Dynamic service that help me create crud services easily.
This is the CRUD service
import { Model, Document, FilterQuery } from 'mongoose';
import { WhereDto } from '../dto/where.dto';
import { IResponseDto } from '../dto/response.dto';
import { MongoError } from 'typeorm';
import {
ArgumentsHost,
Catch,
ExceptionFilter,
InternalServerErrorException,
} from '@nestjs/common';
@Catch(MongoError)
export class CrudService<T extends Document> implements ExceptionFilter {
catch(exception: typeof MongoError, host: ArgumentsHost) {}
constructor(private model: Model<T>) {}
PAGE: number = 0;
PAGE_SIZE: number = 5;
WHERE: WhereDto;
/**
* Find all records that match the given conditions.
*
* @param {WhereDto} where - The conditions to match.
* @return {Promise<IResponseDto<T>>} A promise that resolves to the response DTO.
*/
async findAll(where: WhereDto): Promise<IResponseDto<T>> {
console.log(where);
this.PAGE = where.page;
this.WHERE = where;
delete where.page;
const objectsCount = await this.model.count(where);
const pageCount = Math.ceil(objectsCount / this.PAGE_SIZE);
const data = await this.model
.find(where)
.skip((this.PAGE - 1) * this.PAGE_SIZE)
.limit(this.PAGE_SIZE);
const pagination = {
page: this.PAGE,
pageSize: this.PAGE_SIZE,
pageCount: pageCount,
total: objectsCount,
};
return {
data: data,
meta: {
pagination: pagination,
},
};
}
/**
* Creates a new document in the database.
*
* @param {Partial<T>} data - The data to be used to create the document.
* @return {Promise<T>} The newly created document.
*/
async create(data: Partial<T>): Promise<T> {
console.log('in create');
try {
const doc = new this.model(data);
return doc.save();
} catch (e) {
throw new InternalServerErrorException(
'Error occurred while fetching data from MongoDB.',
e,
);
}
}
/**
* Finds a single record by its id.
*
* @param {string} id - The id of the record to find.
* @return {Promise<IResponseDto<T> | null>} A promise that resolves to the found record or null.
*/
async findOne(id: string): Promise<IResponseDto<T> | null> {
const result = await this.model.findById(id).exec();
if (result) {
// if we want to keep the result as find all as array
const dataArray: T[] = [result]; // Convert the result to an array
return {
data: result,
msg: 'SUCCESS',
};
} else {
return {
msg: 'SUCCESS',
};
}
}
/**
* Updates a document in the database with the given id and data.
*
* @param {string} id - The id of the document to update.
* @param {Partial<T>} data - The data to update the document with.
* @return {Promise<T | null>} A promise that resolves to the updated document, or null if not found.
*/
async update(id: string, data: Partial<T>): Promise<T | null> {
return this.model.findByIdAndUpdate(id, data, { new: true }).exec();
}
/**
* Removes a document from the model by its ID.
*
* @param {string} id - The ID of the document to remove.
* @return {Promise<T | null>} A promise that resolves to the removed document,
* or null if no document was found with the given ID.
*/
async remove(id: string): Promise<T | null> {
return this.model.findByIdAndRemove(id).exec();
}
}
Lets say that there is a country crud and i want to implement it in my CountryService
import { Inject, Injectable } from '@nestjs/common';
import { CreateCountryDto } from './dto/create-country.dto';
import { UpdateCountryDto } from './dto/update-country.dto';
// import { Country } from './entities/country.entity';
import { Model } from 'mongoose';
import { CrudService } from 'src/tools/services/crud.service';
import { WhereDto } from 'src/tools/dto/where.dto';
import { IResponseDto } from 'src/tools/dto/response.dto';
import { Country } from './country.interface';
@Injectable()
export class CountryService {
// constructor(@InjectModel(Country.name) private readonly countryModel: Model<Country>) {}
constructor(
@Inject('COUNTRY_MODEL')
private countryModel: Model<Country>,
) {}
private readonly crudService = new CrudService<Country>(this.countryModel);
async create(data: Partial<Country>): Promise<Country> {
return this.crudService.create(data);
}
async findOne(id: string): Promise<IResponseDto<Country>> {
return this.crudService.findOne(id);
}
async update(id: string, data: Partial<Country>): Promise<Country | null> {
return this.crudService.update(id, data);
}
async remove(id: string): Promise<Country | null> {
return this.crudService.remove(id);
}
async findAll(where: WhereDto): Promise<IResponseDto<Country>> {
return this.crudService.findAll(where);
}
}
To make the CRUD service works perfectly and give a consistent results i add those interfaces
export interface IResponseDto<T> {
readonly msg?: String;
readonly data?: Array<T> | T;
readonly meta?: IMeta;
}
export interface IMeta {
pagination?: IPaginationResponse;
}
export interface IPaginationResponse {
page?: number;
pageSize?: number;
pageCount?: number;
total?: number;
}
Top comments (0)