Search code examples
mongodbtypescriptmongoosetypegoose

Mongodb, Typegoose - generic repository pattern


@typegoose/typegoose": "^7.2.0"

Is there any way to have generic CRUD repository with typescirpt and typegoose? In particular, how to get the method getModelForClass() work with generics? Something similar to this code borrowed from the question 246 .

If so, what am I missing?

import { prop, getModelForClass } from '@typegoose/typegoose';
import { AnyParamConstructor, ReturnModelType } from '@typegoose/typegoose/lib/types';

export class GenericCRUDService<T, U extends AnyParamConstructor<T> = AnyParamConstructor<T>> {
  dataModel: ReturnModelType<U, T>;

  constructor(cls: U) {
    this.dataModel = getModelForClass<T, U>(cls);
  }

  public create(data: T) {
    this.dataModel.create(data);
  }
}

export class Cat {
  @prop()
  public age: number;
  @prop()
  public color: string;
}

export class Dog {
  @prop()
  public isBarking: boolean;
  @prop()
  public race: string;
}

Solution

  • ref: typegoose/typegoose#303

    if you want an "manager class" and not the actual model to be generic, this is easy, you just need to have the right types

    i would recommend to write the class like:

    // NodeJS: 14.4.0
    // MongoDB: 4.2-bionic (Docker)
    import { getModelForClass, prop, types, ReturnModelType, DocumentType } from "@typegoose/typegoose"; // @typegoose/[email protected]
    import * as mongoose from "mongoose"; // [email protected] @types/[email protected]
    
    export class GenericCRUDService<U extends types.AnyParamConstructor<any>> {
      public dataModel: ReturnModelType<U>;
    
      constructor(cls: U) {
        this.dataModel = getModelForClass(cls);
      }
    
      public create(data: mongoose.CreateQuery<DocumentType<InstanceType<U>>>) {
        this.dataModel.create(data);
      }
    }
    
    export class Cat {
      @prop()
      public age?: number;
    
      @prop()
      public color?: string;
    }
    
    export class Dog {
      @prop()
      public isBarking?: boolean;
    
      @prop()
      public race?: string;
    }
    
    (async () => {
      await mongoose.connect(`mongodb://localhost:27017/`, { useNewUrlParser: true, dbName: "verifyMASTER", useCreateIndex: true, useUnifiedTopology: true });
    
      const CatService = new GenericCRUDService(Cat);
    
      await CatService.create({}); // with type support (since ~@types/[email protected])
    
      await mongoose.disconnect();
    })();
    

    (your problem by taking the old example is that since 7.1.0 the "unnecessary" T generic got removed)