deptyped / prisma-extension-pagination

Prisma Client extension for pagination
https://npmjs.com/prisma-extension-pagination
MIT License
227 stars 17 forks source link

NestJS usage #7

Closed gagermaniac closed 1 year ago

gagermaniac commented 1 year ago

Sorry for making an issue here, is there any way to use this extension on nestjs?

I've been using it like this

import pagination from 'prisma-extension-pagination';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$extends(pagination).$connect();
  }

  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }
}

but it doesn't seems work as it say Property 'paginate' does not exist on type 'GetModel<InstancesDelegate<RejectOnNotFound | RejectPerOperation, DefaultArgs>, unknown>'

deptyped commented 1 year ago

This is correct. Because $extends() returns a new Prisma Client instance with the extension applied, rather than modifying the original one.

const prisma = new PrismaClient()

// Declare an extended client that has pagination applied
const prismaA = prisma.$extends(pagination)

// prisma remains unchanged
// prismaA has pagination applied

You need to use a modified instance with the extension applied. Unfortunately, I don't have enough experience with NestJS to know how to do this.

gagermaniac commented 1 year ago

Ah, i see ended up using it like this

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }

  pg() {
    return this.$extends({
      model: {
        $allModels: {
          paginate,
        },
      },
    });
  }
}

and usage

findAll(id: string) {
    return this.prismaService.pg().instances.paginate().withPages({
      limit: 10,
    });
  }

Many Thanks, for this awesome lib

jdavidferreira commented 1 year ago

Ah, i see ended up using it like this

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }

  pg() {
    return this.$extends({
      model: {
        $allModels: {
          paginate,
        },
      },
    });
  }
}

and usage

findAll(id: string) {
    return this.prismaService.pg().instances.paginate().withPages({
      limit: 10,
    });
  }

Many Thanks, for this awesome lib

@gagermaniac Wouldn't that extend the prisma client every time you call the PrismaService.pg() function? I got a workaround for when you want to install extension on certain models, I didn't like it much either though. It consists of extending the prisma client just once per model in the constructor of the model service class.

@Injectable()
export class EntityService {
  private redonly prismaExtended

  constructor(private readonly prisma: PrismaService) {
     this.prismaExtended = this.prisma.$extends({
      model: {
        product: {
          paginate,
        },
      },
    })
  }

  findAllPaginated() {
    return this.prismaExtended.entity
      .paginate()
      .withPages({
        limit: 10,
      })
  }
}

Probably this can be done too to install the extension to all models in the PrismaService installing the extension only once.

jdavidferreira commented 1 year ago

Ok. I think I got it. This installs the extension to all models.

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  public readonly pg

  constructor() {
    super()

    this.pg = this.$extends(pagination)
  }

  async onModuleInit() {
    await this.$connect()
  }
}

Usage:

findAll(id: string) {
    return this.prismaService.pg.instances.paginate().withPages({
      limit: 10,
    });
}
hisabimbola commented 11 months ago

@jdavidferreira did you get the typing to working. When I do this.prismaService.pg.instances I don't get any type interference

btd1337 commented 10 months ago

@jdavidferreira did you get the typing to working. When I do this.prismaService.pg.instances I don't get any type interference

It's necessary to type the variable pg in the PrismaService class, but I don't know what the type is

jdavidferreira commented 10 months ago

@hisabimbola, @btd1337 Sorry, let me fix and improve the example:

import pagination from 'prisma-extension-pagination'

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  public readonly prismaExtended

  constructor() {
    super()

    // This will install the extension to all models. You can also install the extension on some models only, see: https://github.com/deptyped/prisma-extension-pagination#install-extension-on-some-models
    this.prismaExtended = this.$extends(pagination())
  }

  async onModuleInit() {
    await this.$connect()
  }
}

Usage:

@Injectable()
export class ProductsService {
  constructor(private readonly prisma: PrismaService) {}

  findAll() {
    return this.prisma.prismaExtended.product.paginate().withPages({
      limit: 10,
    })
  }
}
btd1337 commented 10 months ago

@jdavidferreira the issue is not this. The real problem is that if you write your code without typing the prismaExtended variable, all type recognition is lost. No changes made in the Prisma schema will be detected or flagged. You won't know if there were breakchanges when you alter your schema. Do you understand?

Therefore, it is necessary to type the prismaExtended variable correctly.

Reference: https://github.com/deptyped/prisma-extension-pagination/issues/19

jdavidferreira commented 10 months ago

@hisabimbola Did you try my new implementation?

My previous example was wrong and the prismaExtended type (previously pg) was not being infered. I fixed the implementation. TypeScript is able to infer the type based on the assignment inside the constructor without typing it at declaration.

Evidence: Animation

btd1337 commented 10 months ago

@hisabimbola Did you try my new implementation?

My previous example was wrong and the prismaExtended type (previously pg) was not being infered. I fixed the implementation. TypeScript is able to infer the type based on the assignment inside the constructor without typing it at declaration.

@jdavidferreira what TypeScript version is your project using? And could you please share your tsconfig?

This behavior is not occurring in my VSCode IDE.

jdavidferreira commented 10 months ago

That could be the issue. I'm using the TypeScript version 5.2.2.

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2020",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strict": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}
btd1337 commented 10 months ago

@jdavidferreira I found the problem:

The property "noImplicitAny": false in tsconfig file.

I removed this property, and autocomplete now works correctly, but the code break with errors:

Captura de Tela 2023-11-21 às 19 26 07
jdavidferreira commented 10 months ago

I removed this property, and autocomplete now works correctly, but the code break with errors:

@btd1337 You have to type all those fields. 😬 It will be tedious but you will have more type safety throughout your project.

JSantangelo-Octopus commented 8 months ago

Hi @jdavidferreira ! Im following this setup :

@hisabimbola, @btd1337 Sorry, let me fix and improve the example:

import pagination from 'prisma-extension-pagination'

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  public readonly prismaExtended

  constructor() {
    super()

    // This will install the extension to all models. You can also install the extension on some models only, see: https://github.com/deptyped/prisma-extension-pagination#install-extension-on-some-models
    this.prismaExtended = this.$extends(pagination())
  }

  async onModuleInit() {
    await this.$connect()
  }
}

Usage:

@Injectable()
export class ProductsService {
  constructor(private readonly prisma: PrismaService) {}

  findAll() {
    return this.prisma.prismaExtended.product.paginate().withPages({
      limit: 10,
    })
  }
}

But the prismaExtended type is not being infered. This is my tsconfig.json (i remove the "noImplicitAny": false value)

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "esModuleInterop": true
  }
}

Demo:

demo_prisma_2.webm

Is there another configuration to check?

AlexRMU commented 5 months ago

Look at this