-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support custom/multiple prisma service (#40)
* add custom prisma service, #31 * provide PrismaClient instance * 0.20.0-dev.1 * add forRootAsync for custom prisma module * inject prisma client into custom prisma service * add useFactory/useClass to forRootAsync * 0.20.0-dev.2 * add custom location and extension docs * add extension example * add extensions example and docs
- Loading branch information
1 parent
e268df7
commit 43e528c
Showing
34 changed files
with
15,758 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
--- | ||
title: Custom Prisma Client Location | ||
layout: ../../layouts/Doc.astro | ||
--- | ||
|
||
Prisma allows you to [customize the output location](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/generating-prisma-client#the-location-of-prisma-client) of your Prisma Client. | ||
|
||
```prisma | ||
generator client { | ||
provider = "prisma-client-js" | ||
output = "../node_modules/@notiz/prisma" | ||
} | ||
``` | ||
|
||
For the schema above you would import `PrismaClient` from `@notiz/prisma` instead of the default `@prisma/client`. | ||
|
||
```diff | ||
-import { PrismaClient } from '@prisma/client'; | ||
+import { PrismaClient } from '@notiz/prisma'; | ||
|
||
const prisma = new PrismaClient(); | ||
``` | ||
|
||
This can be useful if you want to just use a **different location** or want to use **multiple** Prisma Clients in your project. | ||
|
||
## CustomPrismaModule and CustomPrismaService | ||
|
||
To use `nestjs-prisma` with custom output location for Prisma Client, you need to update to `[email protected]` or later and use `CustomPrismaModule` and `CustomPrismaService`. | ||
|
||
Import `CustomPrismaModule` and provide a **unique** name and an **instance** of your `PrismaClient`. The unique name will be used to inject the `CustomPrismaService`. | ||
|
||
```ts | ||
import { Module } from '@nestjs/common'; | ||
import { AppController } from './app.controller'; | ||
import { AppService } from './app.service'; | ||
|
||
import { CustomPrismaModule } from 'nestjs-prisma'; | ||
import { PrismaClient } from '@notiz/prisma'; // 👈 update to your output directory | ||
|
||
@Module({ | ||
imports: [ | ||
CustomPrismaModule.forRoot({ | ||
name: 'PrismaServiceAuth', // 👈 must be unique for each PrismaClient | ||
client: new PrismaClient(), // create new instance of PrismaClient | ||
}), | ||
], | ||
controllers: [AppController], | ||
providers: [AppService], | ||
}) | ||
export class AppModule {} | ||
``` | ||
|
||
The `PrismaClient` instance could also be used together with the new preview feature [Prisma Client extensions](/docs/prisma-client-extensions). | ||
|
||
Use the `Inject` decorator with the **unique** name to inject `CustomPrismaService`. Additionally, `CustomPrismaService` requires you to specify the `PrismaClient` as generic for type-safety & auto-completion. | ||
|
||
```ts | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
|
||
import { CustomPrismaService } from 'nestjs-prisma'; | ||
import { PrismaClient } from '@notiz/prisma'; // 👈 update to your output directory | ||
|
||
@Injectable() | ||
export class AppService { | ||
constructor( | ||
@Inject('PrismaServiceAuth') // 👈 use unique name to reference | ||
private prisma: CustomPrismaService<PrismaClient> // specify PrismaClient for type-safety & auto-completion | ||
) {} | ||
|
||
users() { | ||
return this.prisma.client.user.findMany(); | ||
} | ||
|
||
user(userId: string) { | ||
return this.prisma.client.user.findFirstOrThrow({ | ||
where: { id: userId }, | ||
}); | ||
} | ||
} | ||
``` | ||
|
||
You can use the auto generated queries from `PrismaClient` by accessing `client` property of `CustomPrismaService` e.g. `this.prisma.client.user.create({ data: ... });` | ||
|
||
## Multiple Prisma Clients | ||
|
||
If you have multiple Prisma Clients repeat the above steps for each client. | ||
|
||
Important to register each Prisma Client with a unique name and the correct `PrismaClient` instance. | ||
|
||
```ts | ||
import { Module } from '@nestjs/common'; | ||
import { AppController } from './app.controller'; | ||
import { AppService } from './app.service'; | ||
|
||
import { CustomPrismaModule } from 'nestjs-prisma'; | ||
import { PrismaClient as PrismaAuth } from '@notiz/prisma'; // 👈 update to your output directory | ||
import { PrismaClient as PrismaCms } from '@notiz/prisma-cms'; // 👈 update to your output directory | ||
|
||
@Module({ | ||
imports: [ | ||
CustomPrismaModule.forRoot({ | ||
name: 'PrismaServiceAuth', // 👈 must be unique for each PrismaClient | ||
client: new PrismaAuth(), | ||
}), | ||
CustomPrismaModule.forRoot({ | ||
name: 'PrismaServiceCms', // 👈 must be unique for each PrismaClient | ||
client: new PrismaCms(), | ||
}), | ||
], | ||
controllers: [AppController], | ||
providers: [AppService], | ||
}) | ||
export class AppModule {} | ||
``` | ||
|
||
Import `CustomPrismaService` with the correct reference name and Prisma Client as type. | ||
|
||
```ts | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
|
||
import { CustomPrismaService } from 'nestjs-prisma'; | ||
import { PrismaClient as PrismaAuth } from '@notiz/prisma'; // 👈 update to your output directory | ||
import { PrismaClient as PrismaCms } from '@notiz/prisma-cms'; // 👈 update to your output directory | ||
|
||
@Injectable() | ||
export class AppService { | ||
constructor( | ||
@Inject('PrismaServiceAuth') // 👈 use unique name to reference | ||
private prismaAuth: CustomPrismaService<PrismaAuth>, | ||
@Inject('PrismaServiceCms') // 👈 use unique name to reference | ||
private prismaCms: CustomPrismaService<PrismaCms> | ||
) {} | ||
|
||
users() { | ||
return this.prismaAuth.client.user.findMany(); | ||
} | ||
|
||
user(userId: string) { | ||
return this.prismaAuth.client.user.findFirstOrThrow({ | ||
where: { id: userId }, | ||
}); | ||
} | ||
|
||
posts() { | ||
return this.prismaCms.client.post.findMany(); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
--- | ||
title: Prisma Client Extensions (Preview) | ||
layout: ../../layouts/Doc.astro | ||
--- | ||
|
||
To use the new [Prisma Client Extensions (Preview)](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) you must update to Prisma Client [v4.7.0](https://github.com/prisma/prisma/releases/tag/4.7.0) or later and install `[email protected]` or later. | ||
|
||
Follow this guide or checkout the [extensions example](https://github.com/notiz-dev/nestjs-prisma/tree/main/examples/extensions). | ||
|
||
## Enable preview feature | ||
|
||
Enable `clientExtensions` preview in your Prisma schema and generate Prisma Client again. | ||
|
||
```prisma | ||
datasource db { | ||
provider = "sqlite" | ||
url = env("DATABASE_URL") | ||
} | ||
generator client { | ||
provider = "prisma-client-js" | ||
previewFeatures = ["clientExtensions"] | ||
} | ||
model User { | ||
id String @id @default(cuid()) | ||
email String @unique | ||
name String? | ||
} | ||
``` | ||
|
||
## Prisma Extension | ||
|
||
Create a file for your Prisma extension for example `prisma.extension.ts` | ||
|
||
```ts | ||
import { PrismaClient } from '@prisma/client'; | ||
|
||
export const extendedPrismaClient = new PrismaClient().$extends({ | ||
model: { | ||
user: { | ||
findByEmail: async (email: string) => { | ||
return extendedPrismaClient.user.findFirstOrThrow({ where: { email } }); | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export type extendedPrismaClient = typeof extendedPrismaClient; | ||
``` | ||
|
||
Register your extended Prisma Client using `CustomPrismaModule.forRootAsync`. | ||
|
||
```ts | ||
import { Module } from '@nestjs/common'; | ||
import { AppController } from './app.controller'; | ||
import { AppService } from './app.service'; | ||
|
||
import { CustomPrismaModule } from 'nestjs-prisma'; | ||
import { extendedPrismaClient } from './prisma.extension'; | ||
|
||
@Module({ | ||
imports: [ | ||
// ✅ use `forRootAsync` when using `PrismaClient().$extends({})` | ||
CustomPrismaModule.forRootAsync({ | ||
name: 'PrismaService', | ||
useFactory: () => { | ||
return extendedPrismaClient; | ||
}, | ||
}), | ||
// ❌ error: 'getOwnPropertyDescriptor' on proxy | ||
// CustomPrismaModule.forRoot({ | ||
// name: 'PrismaServiceAuth', | ||
// client: new PrismaClient().$extends({}), | ||
// }), | ||
], | ||
controllers: [AppController], | ||
providers: [AppService], | ||
}) | ||
export class AppModule {} | ||
``` | ||
|
||
Inject `CustomPrismaService` into your controller/service and use the extended Prisma Client for type-safety. | ||
|
||
```ts | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { CustomPrismaService } from 'nestjs-prisma'; | ||
import { extendedPrismaClient } from './prisma.extension'; | ||
|
||
@Injectable() | ||
export class AppService { | ||
constructor( | ||
// ✅ use `prismaClient` from extension for correct type-safety | ||
@Inject('PrismaService') | ||
private prismaService: CustomPrismaService<extendedPrismaClient> | ||
) {} | ||
|
||
users() { | ||
return this.prismaService.client.user.findMany(); | ||
} | ||
|
||
user(email: string) { | ||
// 🦾 use new `findByEmail` | ||
return this.prismaService.client.user.findByEmail(email); | ||
} | ||
} | ||
``` | ||
|
||
Now you have access to your extensions `this.prismaService.client.user.findByEmail(email)`. | ||
|
||
## Type issues with Prisma Client v4.7.0 | ||
|
||
Change `declaration` to `false` in your `tsconfig.json` - workaround for https://github.com/prisma/prisma/issues/16536#issuecomment-1332055501 | ||
|
||
`The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module.exports = { | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: 'tsconfig.json', | ||
tsconfigRootDir : __dirname, | ||
sourceType: 'module', | ||
}, | ||
plugins: ['@typescript-eslint/eslint-plugin'], | ||
extends: [ | ||
'plugin:@typescript-eslint/recommended', | ||
'plugin:prettier/recommended', | ||
], | ||
root: true, | ||
env: { | ||
node: true, | ||
jest: true, | ||
}, | ||
ignorePatterns: ['.eslintrc.js'], | ||
rules: { | ||
'@typescript-eslint/interface-name-prefix': 'off', | ||
'@typescript-eslint/explicit-function-return-type': 'off', | ||
'@typescript-eslint/explicit-module-boundary-types': 'off', | ||
'@typescript-eslint/no-explicit-any': 'off', | ||
}, | ||
}; |
Oops, something went wrong.