Where is the beest place to put prisma middleware on NestJs?

Viewed 884

My prisma.service.ts looks like this:

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

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

According to Prisma docs I am supposed to put them outside the context of the request handler. That would be the app that I create on main.ts. Putting a middleware there before the app itself is defined looks wierd to me and doesn't work. I'd prefer to put it on the prisma.service.ts file itself

1 Answers

Not sure whether this is the "best" place to register them, but we do it in the constructor of the service along with the logging configuration and it works:

import { INestApplication, Injectable, Logger, OnModuleInit } from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";
import { ConcurrencyErrorMiddleware } from "./concurrency-error.middleware";

@Injectable()
export class PrismaService extends PrismaClient<Prisma.PrismaClientOptions, "query"> implements OnModuleInit {
  private readonly logger = new Logger(PrismaService.name);

  constructor() {
    super({ log: [{ emit: "event", level: "query" }] });

    this.logger.log(`Prisma v${Prisma.prismaVersion.client}`);
    this.$on("query", (e) => this.logger.debug(`${e.query} ${e.params}`));

    this.$use(ConcurrencyErrorMiddleware());
  }

  async onModuleInit(): Promise<void> {
    await this.$connect();
  }

  async enableShutdownHooks(app: INestApplication): Promise<void> {
    this.$on("beforeExit", async () => {
      await app.close();
    });
  }
}
// An example of such a middleware.
import { Prisma } from "@prisma/client";

export function ConcurrencyErrorMiddleware<T extends Prisma.BatchPayload = Prisma.BatchPayload>(): Prisma.Middleware {
  return async (params: Prisma.MiddlewareParams, next: (params: Prisma.MiddlewareParams) => Promise<T>): Promise<T> => {
    const result = await next(params);
    if (
      (params.action === "updateMany" || params.action === "deleteMany") &&
      params.args.where.version &&
      result.count === 0
    ) {
      throw new ConcurrencyError();
    }
    return result;
  };
}
Related