0

I have been looking into NestJS recently and started some sample project. This project uses MongoDB, so I also chose Typegoose to make this happen. I found a tutorial online (https://nartc.netlify.app/blogs/nestjs-typegoose/) that describes how to use some abstractions with base typegoose models.

So, my base.service.ts now looks like this:

import { InternalServerErrorException } from '@nestjs/common';
import { DocumentType, ReturnModelType } from '@typegoose/typegoose';
import { AnyParamConstructor } from '@typegoose/typegoose/lib/types';
import { MongoError } from 'mongodb';
import { DocumentQuery, Types, Query, UpdateQuery } from 'mongoose';

import { BaseModel } from './base.model';

type QueryList<T extends BaseModel> = DocumentQuery<
  Array<DocumentType<T>>,
  DocumentType<T>
>;
type QueryItem<T extends BaseModel> = DocumentQuery<
  DocumentType<T>,
  DocumentType<T>
>;

export abstract class BaseService<T extends BaseModel> {
  protected model: ReturnModelType<AnyParamConstructor<T>>;

  protected constructor(model: ReturnModelType<AnyParamConstructor<T>>) {
    this.model = model;
  }

  protected static throwMongoError(err: MongoError): void {
    throw new InternalServerErrorException(err, err.errmsg);
  }

  protected static toObjectId(id: string): Types.ObjectId {
    try {
      return Types.ObjectId(id);
    } catch (e) {
      this.throwMongoError(e);
    }
  }

  createModel(doc?: Partial<T>): T {
    return new this.model(doc);
  }

  findAll(filter = {}): QueryList<T> {
    return this.model.find(filter);
  }

  async findAllAsync(filter = {}): Promise<Array<DocumentType<T>>> {
    try {
      return await this.findAll(filter).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  findOne(filter = {}): QueryItem<T> {
    return this.model.findOne(filter);
  }

  async findOneAsync(filter = {}): Promise<DocumentType<T>> {
    try {
      return await this.findOne(filter).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  findById(id: string): QueryItem<T> {
    return this.model.findById(BaseService.toObjectId(id));
  }

  async findByIdAsync(id: string): Promise<DocumentType<T>> {
    try {
      return await this.findById(id).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  async create(item: T): Promise<DocumentType<T>> {
    try {
      return await this.model.create(item);
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  delete(filter = {}): QueryItem<T> {
    return this.model.findOneAndDelete(filter);
  }

  async deleteAsync(filter = {}): Promise<DocumentType<T>> {
    try {
      return await this.delete(filter).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  deleteById(id: string): QueryItem<T> {
    return this.model.findByIdAndDelete(BaseService.toObjectId(id));
  }

  async deleteByIdAsync(id: string): Promise<DocumentType<T>> {
    try {
      return await this.deleteById(id).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  update(item: T): QueryItem<T> {
    return this.model.findByIdAndUpdate(BaseService.toObjectId(item.id), item, {
      new: true
    });
  }

  async updateAsync(item: T): Promise<DocumentType<T>> {
    try {
      return await this.update(item).exec();
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }

  count(filter = {}): Query<number> {
    return this.model.count(filter);
  }

  async countAsync(filter = {}): Promise<number> {
    try {
      return await this.count(filter);
    } catch (e) {
      BaseService.throwMongoError(e);
    }
  }
}

This is exact same code from the blog post mentioned above. However, I am getting some errors:

No overload matches this call.
  The last overload gave the following error.
    Argument of type 'T' is not assignable to parameter of type 'UpdateQuery<DocumentType<T>>'.
      Type 'BaseModel' is not assignable to type 'UpdateQuery<DocumentType<T>>'.
        Type 'BaseModel' is not assignable to type '_UpdateQuery<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & DotAndArrayNotation<...>'.
          Type 'BaseModel' is not assignable to type 'ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>>'.
            Type 'T' is not assignable to type '_UpdateQuery<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & DotAndArrayNotation<...>'.
              Type 'BaseModel' is not assignable to type '_UpdateQuery<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>> & DotAndArrayNotation<...>'.
                Type 'BaseModel' is not assignable to type 'ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>>'.
                  Type 'T' is not assignable to type 'ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>>'.
                    Type 'BaseModel' is not assignable to type 'ReadonlyPartial<_AllowStringsForIds<LeanDocument<DocumentType<T>>>>'.

This error points to update method, into item parameter inside this.model.findByIdAndUpdate(BaseService.toObjectId(item.id), item, ...); I understand that the type of item is <T extends BaseModel> and it works on the blog post, but why do I get an error over here?

Also, this error is also present:

Generic type 'Query<ResultType, DocType, THelpers>' requires between 2 and 3 type arguments.

on count method, in the Query<number>.

The one thing I do not get is why do these errors appear on my project, but not on the tutorial mentioned before?

1 Answers1

0

This error seems like you have the unofficial and the official types installed OR the unofficial types with incompatible typegoose

the unofficial types are @types/mongoose
the official types are provided since mongoose 5.10.19 (but only working since ~5.11.18)

typegoose 8.0.0-beta.x currently only supports the official types, look here on which versions in the beta are supported [this is the beta branch]

hasezoey
  • 998
  • 9
  • 24
  • I have created a blank project and installed the dependencies of the versions you have specified. Still the same error. – Gvidas Pranauskas May 11 '21 at 20:30
  • Also, please have a look at this tutorial: https://nartc.netlify.app/blogs/nestjs-typegoose/. I found it here: https://stackoverflow.com/questions/59547243/create-dtos-bos-and-daos-for-nestjs-rest-api. I just want a good structure to my project and want to follow best practices regarding Typegoose and NestJS but nothing seems to work. – Gvidas Pranauskas May 11 '21 at 20:32
  • i dont know much about nestjs, but i know there is an project called "nestjs-typegoose" that tries to be better with typegoose than "@nestjs/mongoose" is --- if it is still the same error, then it means you are using mongoose with the official types, could you maybe update your question with the versions you are using and mark the line on where the error happens? (and / or make an reproduction repository?) – hasezoey May 12 '21 at 08:50
  • After many reading and searching hours I have decided to use other database instead of mongodb and its implementations in nestjs/typescript. The project I have planned also will use a lot of relational data, so relational database will suit this project better, too. I just could not solve the issue and gave up pretty much. But its not the only motivation regarding this decision - I just feel like trying new things. Also, the features I wanted to implement was not necessary, although, they would have been nice to have. Thank you for your help! – Gvidas Pranauskas May 12 '21 at 16:12