mongoose-paginate-v2 icon indicating copy to clipboard operation
mongoose-paginate-v2 copied to clipboard

Complete Example and explanation of usage with typescript

Open herbertpimentel opened this issue 2 years ago • 7 comments

Is your feature request related to a problem? Please describe. The typescript snipet provided on readme is confuse and I was no able to make it work properly

Describe the solution you'd like Have a clear, complete and explained example of typescript usage of type definition. Including the missing imports from the reademe page.

herbertpimentel avatar Mar 22 '22 00:03 herbertpimentel

Also if possible please provide an example to integrate with Nest js. Many Thanks!

nickchauhan avatar Mar 23 '22 08:03 nickchauhan

@nickchauhan I am using like this... this works... but has this type definition problem...

I define a Model like so:

import * as mongoose from 'mongoose';

import * as mongoosePaginate from 'mongoose-paginate-v2';

interface MyType {
  name: string;
};

const schemaOptions = {
  timestamps: true,
};

const schema = new mongoose.Schema(
  {
    name: { type: String, trim: true, required: true },
  },
  schemaOptions
);

schema.plugin(mongoosePaginate);

schema.set('toObject', {
  getters: true,
  virtuals: true,
});

schema.set('versionKey', false);

export default mongoose.model<MyType>('MyType', schema);

then I just need to cast it to any due the typescript complain about the model do not have the paginate method


(this.myTypeCollection as any).paginate({});

herbertpimentel avatar Mar 23 '22 14:03 herbertpimentel

@herbertpimentel Can you try as below?

import * as mongoose from 'mongoose';
import * as mongoosePaginate from 'mongoose-paginate-v2';

import { Document, PaginateModel } from "mongoose";

interface MyType {
  name: string;
};

const schemaOptions = {
  timestamps: true,
};

const schema = new mongoose.Schema(
  {
    name: { type: String, trim: true, required: true },
  },
  schemaOptions
);

schema.plugin(mongoosePaginate);

schema.set('toObject', {
  getters: true,
  virtuals: true,
});

schema.set('versionKey', false);

// declare a mongoose document based on a Typescript interface representing your schema
interface MyTpyeDocument extends Document, MyType {}

// create the paginated model
const model = mongoose.model<MyTpyeDocument, PaginateModel<MyTpyeDocument>>(
  "MyType",
  schema,
  "mytype"
);

export default model;

after that, you can paginate

import model from './schema';

const query = {
    name : 'test'
}

model.paginate(query, {limit: 5, page: 1}).then(result => {
    console.log(result);
});

I hope it helps!

nickchauhan avatar Mar 24 '22 09:03 nickchauhan

Hello @aravindnc

I have this issue too but a slight difference in my request is that I want to dynamically type the items in the docs array. I do not want to use the ModelType. I want to be able to be able to pass a generic type when I call paginate which will become the type for the items in docs. I want to do this because in a paginate query, one can decide to deselect some fields (using select) and would like to type the response to fit. Another case like populate also exists. Is there any way that I can do that? I will be glad to contribute if need be. Thanks.

orimdominic avatar Sep 15 '23 15:09 orimdominic

Looking at the code at paginate we can achieve this by

interface PaginateModel<T, TQueryHelpers = {}, TMethods = {}>
  extends Model<T, TQueryHelpers, TMethods> {
  paginate<O extends PaginateOptions = PaginateOptions, UserType = T>(
    query?: FilterQuery<T>,
    options?: O,
    callback?: (
      err: any,
      result: PaginateResult<PaginateDocument<UserType, TMethods, O>>
    ) => void
  ): Promise<PaginateResult<PaginateDocument<UserType, TMethods, O>>>;
}

where we use default generic parameters so a user can use it like so

UserModel.paginate<PaginateOptions, TypeForDocsArrayItem>(...)

They use PaginateOptions when they do not have any type to pass in, as in the example above. The benefit of this option is that the API of the paginate function is kept consistent.

OR

we can use an overload so that the user can avoid passing in PaginationOptions before TypeForDocsArrayItem like so

interface PaginateModel<T, TQueryHelpers = {}, TMethods = {}>
  extends Model<T, TQueryHelpers, TMethods> {
  paginate<O extends PaginateOptions = PaginateOptions, UserType = T>(
    query?: FilterQuery<T>,
    options?: O,
    callback?: (
      err: any,
      result: PaginateResult<PaginateDocument<UserType, TMethods, O>>
    ) => void
  ): Promise<PaginateResult<PaginateDocument<UserType, TMethods, O>>>;
}

// the overload
interface PaginateModel<T, TQueryHelpers = {}, TMethods = {}>
  extends Model<T, TQueryHelpers, TMethods> {
  paginate<UserType = T, O extends PaginateOptions = PaginateOptions>(
    query?: FilterQuery<T>,
    options?: O,
    callback?: (
      err: any,
      result: PaginateResult<PaginateDocument<UserType, TMethods, O>>
    ) => void
  ): Promise<PaginateResult<PaginateDocument<UserType, TMethods, O>>>;
}

I'll be glad to know what you think. Thank you.

orimdominic avatar Sep 15 '23 15:09 orimdominic

@herbertpimentel Can you try as below?

import * as mongoose from 'mongoose';
import * as mongoosePaginate from 'mongoose-paginate-v2';

import { Document, PaginateModel } from "mongoose";

interface MyType {
  name: string;
};

const schemaOptions = {
  timestamps: true,
};

const schema = new mongoose.Schema(
  {
    name: { type: String, trim: true, required: true },
  },
  schemaOptions
);

schema.plugin(mongoosePaginate);

schema.set('toObject', {
  getters: true,
  virtuals: true,
});

schema.set('versionKey', false);

// declare a mongoose document based on a Typescript interface representing your schema
interface MyTpyeDocument extends Document, MyType {}

// create the paginated model
const model = mongoose.model<MyTpyeDocument, PaginateModel<MyTpyeDocument>>(
  "MyType",
  schema,
  "mytype"
);

export default model;

after that, you can paginate

import model from './schema';

const query = {
    name : 'test'
}

model.paginate(query, {limit: 5, page: 1}).then(result => {
    console.log(result);
});

I hope it helps!

I'm doing exactly this, however the docs in the response to the Model.paginate method are typed as HydratedDocument<T, TMethods, TVirtuals>[] and not my model's interface. Any idea how I can solve that? Here's a snippet:

const { docs, nextPage } = await MyModel.paginate()

// docs here is not typed as the type of MyModel

I've also tried MyModel.paginate<MyModelType>()

tdsoundation avatar Nov 27 '23 15:11 tdsoundation

Hello @tdsoundation Have you taken a look at this article - Set Custom Types for docs items in mongoose-paginate-v2

orimdominic avatar Nov 28 '23 06:11 orimdominic