Giter Site home page Giter Site logo

Comments (7)

marcjulian avatar marcjulian commented on June 26, 2024 1

Hi @MorenoMdz, this is already possible with Prisma Client Extension. I have added it to the Extensions Example.

import { Prisma, PrismaClient } from '@prisma/client';
// used for Read Replicas example
import { readReplicas } from '@prisma/extension-read-replicas';
export const extendedPrismaClient = new PrismaClient<
Prisma.PrismaClientOptions,
'query' | 'info' | 'warn' | 'error'
>({
log: [
{ level: 'query', emit: 'event' },
{ level: 'info', emit: 'event' },
{ level: 'warn', emit: 'event' },
{ level: 'error', emit: 'event' },
],
})
// FIXME - `$on()` returns void
// .$on('error', (e) => {})
.$extends({
model: {
user: {
findByEmail: async (email: string) => {
console.log('extension findByEmail');
return extendedPrismaClient.user.findFirstOrThrow({
where: { email },
});
},
},
},
})
// Read Replicas example, change datasource prodiver (prisma/schema.prisma) to a supported database
// More details in the blog - https://www.prisma.io/blog/read-replicas-prisma-client-extension-f66prwk56wow
// .$extends(
// readReplicas({
// url: 'postgres://localhost:5432/prisma',
// }),
// );
export type extendedPrismaClient = typeof extendedPrismaClient;

Let me know if this helps you. I will add a section to the docs too.

from nestjs-prisma.

MorenoMdz avatar MorenoMdz commented on June 26, 2024 1

This is great, I will give it a shot today, thanks again @marcjulian !

from nestjs-prisma.

marcjulian avatar marcjulian commented on June 26, 2024

Checkout the new section for Read Replica in the docs.

from nestjs-prisma.

MorenoMdz avatar MorenoMdz commented on June 26, 2024

@marcjulian I might have to redo our setup, currently we have a PrismaConfigService.ts which we inject in all our services:

import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { PrismaOptionsFactory, PrismaServiceOptions } from 'nestjs-prisma'
import { getEnvironment, isProduction, isDevelopment } from './utils'

@Injectable()
export class PrismaConfigService implements PrismaOptionsFactory {
  constructor(private configService: ConfigService) {}

  createPrismaOptions(): PrismaServiceOptions | Promise<PrismaServiceOptions> {
    const environment = getEnvironment()
    const isProd = environment === 'production'
    const urlSuffix = isProduction()
      ? '_PROD'
      : // When running locally, and we want to use the DEV database
      isDevelopment() || process.env.FIREBASE_ENV === 'DEV'
      ? '_DEV'
      : ''
    const DB_URL = this.configService.get(`DATABASE_URL${urlSuffix}`)

    return {
      prismaOptions: {
        log: isProd ? undefined : ['info', 'query'],
        datasources: {
          db: {
            url: DB_URL,
          },
        },
      },
      explicitConnect: this.configService.get('DB_EXPLICIT_CONNECT') || false,
    }
  }
}

And then the app.module is initialized like this:

... 
imports: [
    PrismaModule.forRootAsync({
      isGlobal: true,
      useClass: PrismaConfigService,
    }),
    ...
    ]

Should I just redo the setup and or try to plug in the extensions on top of this at the App.module imports section?

from nestjs-prisma.

marcjulian avatar marcjulian commented on June 26, 2024

You will need to use CustomPrismaModule instead of PrismaModule. The reason is, that PrismaModule provides PrismaService which is extended by the default PrismaClient and it cannot be used together with $extends.

Use CustomPrismaModule and inject your ConfigService which you can access in the factory method and pass any options directly to your PrismaClient instance.

import { Module } from '@nestjs/common';
import { CustomPrismaModule } from 'nestjs-prisma';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { extendedPrismaClient } from './prisma.extension';

@Module({
  imports: [
    CustomPrismaModule.forRootAsync({
      name: 'PrismaService',
      useFactory: (config: ConfigService) => {
        const options = {};
        return extendedPrismaClient(options);
      },
      inject: [ConfigService],
      // import ConfigModule when `isGlobal` not true
      // imports: [ConfigModule],
    }),
    ConfigModule.forRoot({
      isGlobal: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
import { Prisma, PrismaClient } from '@prisma/client';
// used for Read Replicas example
import { readReplicas } from '@prisma/extension-read-replicas';

// add more parameters like read replicas urls
export const extendedPrismaClient = (options?: Prisma.PrismaClientOptions, ...) =>
  new PrismaClient<
    Prisma.PrismaClientOptions,
    'query' | 'info' | 'warn' | 'error'
  >(options)
// .$extends(
//   readReplicas({
//     url: 'postgres://localhost:5432/prisma',
//   }),
// );

// add ReturnType because extendedPrismaClient is now a function
export type extendedPrismaClient = ReturnType<typeof extendedPrismaClient>;

Let me know if that helps. I might need to update the docs to make it more clear to use CustomPrismaModule and CustomPrismaService when using $extends.

from nestjs-prisma.

MorenoMdz avatar MorenoMdz commented on June 26, 2024

Thanks again for the response. I am still not sure I get how to configure this with our current setup.

  1. Do I keep the current PrismaConfigService file that is creating the Prisma client options as well as defining the DB_URL? If so, ideally we want to define the read replica URL here too instead of at the extension file. What I am not sure is when using an extension, where do we configure the base Prisma settings? Would I have the setup in all three places, the PrismaConfigService, App module, and the new Prisma extension file?

  2. Do I have to change all places that are currently loading the PrismaService to access the db to use CustomPrismaService<extendedPrismaClient> instead? Seems very verbose, shouldn't PrismaService already access the instance of the custom service everywhere?

Also, the way we have been injecting the Prisma service in our services is like private prisma: PrismaService I would assume that @Inject('PrismaService') is the same?

from nestjs-prisma.

marcjulian avatar marcjulian commented on June 26, 2024

To clear it up a bit more.

PrismaModule and PrismaService

  1. PrismaService has only access to the default PrismaClient, it does not work with $extends at all.
    Because PrismaService extends PrismaClient, the PrismaClient is created and it cannot be $extends further.
import { Inject, Injectable, Optional } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaServiceOptions } from './interfaces';
import { PRISMA_SERVICE_OPTIONS } from './prisma.constants';

@Injectable()
export class PrismaService extends PrismaClient {
  constructor(
    @Optional()
    @Inject(PRISMA_SERVICE_OPTIONS)
    private readonly prismaServiceOptions: PrismaServiceOptions = {},
  ) {
    super(prismaServiceOptions.prismaOptions);

    ...
  }
 
  ...
}
  1. PrismaModule allows passing options to the PrismaClient via forRoot or forRootAsync. forRootAsync allows to pass a config class e.g. PrismaConfigService
  2. Use PrismaService to access the database

CustomPrismaModule and CustomPrismaService

  1. Use CustomPrismaModule and CustomPrismaService when you need to use $extends(...)
import { PrismaClientLike } from './custom-prisma-options';
import { Inject, Injectable } from '@nestjs/common';
import { CUSTOM_PRISMA_CLIENT } from './custom-prisma.constants';

@Injectable()
export class CustomPrismaService<Client extends PrismaClientLike> {
  constructor(
    @Inject(CUSTOM_PRISMA_CLIENT)
    public client: Client,
  ) {}
}

CustomPrismaService gives you access to your extended PrismaClient.

  1. You have access to the PrismaClient instance directly and you can pass your options to it new PrismaClient(YOUR_OPTIONS)
  2. Use CustomPrismaService to access the database
  3. You can provide a config class to CustomPrismaModule
import { ExtendedPrismaConfigService } from './extended-prisma-config.service';

// app.module.ts
CustomPrismaModule.forRootAsync({
      name: 'PrismaService',
      useClass: ExtendedPrismaConfigService,
})


// extended-prisma-config.service.ts
import { Injectable } from '@nestjs/common';
import { CustomPrismaClientFactory } from 'nestjs-prisma';
import { extendedPrismaClient } from './prisma.extension';

@Injectable()
export class ExtendedPrismaConfigService
  implements CustomPrismaClientFactory<extendedPrismaClient>
{
  constructor() {
    // inject config service here
  }

  createPrismaClient(): extendedPrismaClient {
    return extendedPrismaClient;
  }
}

CustomPrismaModule does not provide the extended PrismaClient for PrismaService, you must use CustomPrismaService

I will move this into a discussion as this might help others too.

from nestjs-prisma.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.