1.0.2 • Published 5 months ago

prisma-extension-find-and-count v1.0.2

Weekly downloads
-
License
-
Repository
-
Last release
5 months ago

This extension for Prisma provides findAndCount method doing findMany and count in one transaction to avoid difference in data.\ All you need is to extend prisma in singleton

import { findAndCountExtension } from 'prisma-extension-find-and-count'
...
MyPrismaClientSingleton.$extends(findAndCountExtension)

and then use it

// options are options for findMany like 'where', 'data', 'select', etc.
this.prisma.someModel.findAndCount(options)

If you're looking for how to setup Prisma as singleton to avoid different contexts and instances of PrismaClient, here what I've got for NestJS (thanks to @micobarac answer in https://github.com/prisma/prisma/issues/18628#issuecomment-2655850811)

folder structure

src
--prisma
----prisma.extensions.ts
----prisma.module.ts
----prisma.provides.ts
----prisma.service.ts
app.module.ts

now content\ prisma.extensions.ts - here we can define our extension if we don't want to pull them from npm

import { Prisma } from '@prisma/client'

export const existsExtension = Prisma.defineExtension({
  name: 'exists-extension',
  model: {
    $allModels: {
      async exists<T>(
        this: T,
        where: Prisma.Args<T, 'findFirst'>['where'],
      ): Promise<boolean> {
        const context = Prisma.getExtensionContext(this)
        const count = await (context as any).count({
          where,
          take: 1,
        } as Prisma.Args<T, 'count'>)
        return count > 0
      },
    },
  },
})

export const softDeleteExtension = Prisma.defineExtension({
  name: 'soft-delete-extension',
  model: {
    $allModels: {
      async softDelete<T>(
        this: T,
        where: Prisma.Args<T, 'update'>['where'],
      ): Promise<Prisma.Result<T, unknown, 'update'>> {
        const context = Prisma.getExtensionContext(this)
        return await (context as any).update({
          where,
          data: {
            deletedAt: new Date(),
          },
        } as Prisma.Args<T, 'update'>)
      },
    },
  },
})

export const findAndCountExtension = Prisma.defineExtension((client) => {
  return client.$extends({
    name: 'find-and-count-extension',
    model: {
      $allModels: {
        async findAndCount<Model, Args>(
          this: Model,
          args: Prisma.Exact<Args, Prisma.Args<Model, 'findMany'>>,
        ): Promise<[Prisma.Result<Model, Args, 'findMany'>, number]> {
          return await client.$transaction([
            (this as any).findMany(args),
            (this as any).count({ where: (args as any).where }),
          ])
        },
      },
    },
  })
})

prisma.provides.ts - here we turn on our extensions through withExtensions method

import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'
import { PrismaClient } from '@prisma/client'
import {
  existsExtension,
  findAndCountExtension,
  softDeleteExtension,
} from './prisma.extensions'

@Injectable()
export class PrismaProvider
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  private static initialized = false

  async onModuleInit() {
    if (!PrismaProvider.initialized) {
      PrismaProvider.initialized = true
      await this.$connect()
    }
  }

  async onModuleDestroy() {
    if (PrismaProvider.initialized) {
      PrismaProvider.initialized = false
      await this.$disconnect()
    }
  }

  withExtensions() {
    return this.$extends(existsExtension)
      .$extends(softDeleteExtension)
      .$extends(findAndCountExtension)
  }
}

prisma.service.ts

import { Injectable, Type } from '@nestjs/common'
import { PrismaProvider } from './prisma.provider'

const ExtendedPrismaClient = class {
  constructor(provider: PrismaProvider) {
    return provider.withExtensions()
  }
} as Type<ReturnType<PrismaProvider['withExtensions']>>

@Injectable()
export class PrismaService extends ExtendedPrismaClient {
  constructor(provider: PrismaProvider) {
    super(provider)
  }
}

prisma.module.ts - @Global decorator give us ability to import PrismaModule once and make it available in any other module

import { Global, Module } from '@nestjs/common'
import { PrismaService } from './prisma.service'
import { PrismaProvider } from './prisma.provider'

@Global()
@Module({
  providers: [PrismaProvider, PrismaService],
  exports: [PrismaProvider, PrismaService],
})
export class PrismaModule {}

app.module.ts - don't forget to import PrismaModule here to make it app-wide

import { PrismaModule } from './prisma/prisma.module'
...
@Module({
  imports: [
    PrismaModule,
...