DEV Community

Cover image for Creating a Dynamic service for NestJS and mongoose

Creating a Dynamic service for NestJS and mongoose

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(); } } 
Enter fullscreen mode Exit fullscreen mode

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); } } 
Enter fullscreen mode Exit fullscreen mode

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; } 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)