@koalarx/nest 1.18.21 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/docs/00-cli-reference.md +201 -0
- package/docs/01-guia-instalacao.md +113 -0
- package/docs/02-configuracao-inicial.md +176 -0
- package/docs/04-tratamento-erros.md +303 -0
- package/docs/05-features-avancadas.md +969 -0
- package/docs/06-decoradores.md +220 -0
- package/docs/07-guia-bun.md +176 -0
- package/docs/08-prisma-client.md +487 -0
- package/docs/09-mcp-vscode-extension.md +437 -0
- package/docs/EXAMPLE.md +1671 -0
- package/docs/README.md +59 -0
- package/mcp-server/mcp.json.example +27 -0
- package/mcp-server/server.d.ts +2 -0
- package/mcp-server/server.d.ts.map +1 -0
- package/mcp-server/server.js +248 -0
- package/mcp-server/server.js.map +1 -0
- package/package.json +2 -1
- package/tsconfig.lib.tsbuildinfo +1 -1
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# Integração com Prisma
|
|
2
|
+
|
|
3
|
+
## Visão Geral
|
|
4
|
+
|
|
5
|
+
A biblioteca `@koalarx/nest` foi projetada para funcionar de forma **totalmente automática e transparente** com o Prisma Client do seu projeto. Não é necessária nenhuma configuração adicional além da geração padrão do cliente Prisma.
|
|
6
|
+
|
|
7
|
+
## Configuração Automática
|
|
8
|
+
|
|
9
|
+
### Pré-requisitos
|
|
10
|
+
|
|
11
|
+
1. Você deve ter um esquema Prisma configurado (`prisma/schema.prisma`)
|
|
12
|
+
2. O cliente Prisma deve ser gerado:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bunx prisma generate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Como Funciona
|
|
19
|
+
|
|
20
|
+
A biblioteca **resolve automaticamente** o `PrismaClient` do seu projeto através de um sistema inteligente de busca:
|
|
21
|
+
|
|
22
|
+
#### 1. Busca Automática de Caminhos
|
|
23
|
+
|
|
24
|
+
O `PrismaService` procura o cliente Prisma em múltiplos locais padrão:
|
|
25
|
+
|
|
26
|
+
- `prisma/generated/client.js` (ou `.ts`)
|
|
27
|
+
- `prisma/generated/index.js` (ou `.ts`)
|
|
28
|
+
- Diretórios relativos ao `process.cwd()`
|
|
29
|
+
- Diretórios relativos ao arquivo principal da aplicação
|
|
30
|
+
|
|
31
|
+
#### 2. Suporte Transparente
|
|
32
|
+
|
|
33
|
+
Uma vez encontrado, o cliente é carregado **dinamicamente** e exposto através de um `Proxy` transparente que:
|
|
34
|
+
|
|
35
|
+
- Permite acesso direto às models (ex: `prisma.person.findFirst()`)
|
|
36
|
+
- Preserva todos os métodos do PrismaClient (ex: `$queryRaw`, `$transaction`)
|
|
37
|
+
- Mantém compatibilidade total com transações
|
|
38
|
+
|
|
39
|
+
#### 3. Sem Configuração Necessária
|
|
40
|
+
|
|
41
|
+
✅ Não é necessário:
|
|
42
|
+
- Configurar path mappings
|
|
43
|
+
- Definir variáveis de ambiente de caminho
|
|
44
|
+
- Registrar o cliente manualmente
|
|
45
|
+
|
|
46
|
+
Tudo funciona **out-of-the-box**!
|
|
47
|
+
|
|
48
|
+
## Transações
|
|
49
|
+
|
|
50
|
+
### Usando withTransaction
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
async executeTransaction() {
|
|
54
|
+
return this.prisma.withTransaction(async (client) => {
|
|
55
|
+
// client é um Prisma.TransactionClient
|
|
56
|
+
await client.person.create({ data: {...} })
|
|
57
|
+
await client.address.create({ data: {...} })
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Em Repositórios
|
|
63
|
+
|
|
64
|
+
O `RepositoryBase` fornece suporte built-in para transações:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
async saveMultiple(entities: Person[]) {
|
|
68
|
+
return this.withTransaction(async (client) => {
|
|
69
|
+
for (const entity of entities) {
|
|
70
|
+
await client.person.create({ data: this.entityToPrisma(entity) })
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Acessar Tipos do DBContext em Repositórios
|
|
77
|
+
|
|
78
|
+
Ao estender `RepositoryBase`, você pode acessar o `DBContext` (que pode ser `PrismaClient` ou `DbTransactionContext` em transações) de duas formas:
|
|
79
|
+
|
|
80
|
+
#### Via Construtor com Configuração
|
|
81
|
+
|
|
82
|
+
Passe o DBContext e uma string indicando qual model Prisma usar. Para ter type safety completo com os tipos do DBContext, use 3 type parameters:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { RepositoryBase } from '@koalarx/nest/core/database/repository.base'
|
|
86
|
+
import { Person } from '@/domain/entities/person/person'
|
|
87
|
+
import { IPersonRepository } from '@/domain/repositories/iperson.repository'
|
|
88
|
+
import { DbTransactionContext } from '../db-transaction-context'
|
|
89
|
+
import { PRISMA_TOKEN } from '@koalarx/nest/core/koala-nest-database.module'
|
|
90
|
+
import { Inject, Injectable } from '@nestjs/common'
|
|
91
|
+
|
|
92
|
+
@Injectable()
|
|
93
|
+
export class PersonRepository
|
|
94
|
+
extends RepositoryBase<Person, DbTransactionContext, 'person'>
|
|
95
|
+
implements IPersonRepository
|
|
96
|
+
{
|
|
97
|
+
constructor(
|
|
98
|
+
@Inject(PRISMA_TOKEN)
|
|
99
|
+
prisma: DbTransactionContext,
|
|
100
|
+
) {
|
|
101
|
+
super({
|
|
102
|
+
modelName: Person, // Seu modelo/entidade
|
|
103
|
+
context: prisma, // DBContext injetado
|
|
104
|
+
include: { // Includes opcionais
|
|
105
|
+
phones: true,
|
|
106
|
+
address: true,
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Seus métodos de repositório
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Type Parameters do RepositoryBase**:
|
|
116
|
+
- `Person` - Tipo da entidade
|
|
117
|
+
- `DbTransactionContext` - Tipo do DBContext (PrismaClient ou DbTransactionContext)
|
|
118
|
+
- `'person'` - String literal do model Prisma (deve corresponder ao nome da tabela/model)
|
|
119
|
+
|
|
120
|
+
#### Acessar o Contexto dentro de Métodos
|
|
121
|
+
|
|
122
|
+
Use `this.context()` para acessar o DBContext dentro de qualquer método. Com os 3 type parameters, você terá type safety completo:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
@Injectable()
|
|
126
|
+
export class PersonRepository
|
|
127
|
+
extends RepositoryBase<Person, DbTransactionContext, 'person'>
|
|
128
|
+
implements IPersonRepository
|
|
129
|
+
{
|
|
130
|
+
constructor(
|
|
131
|
+
@Inject(PRISMA_TOKEN)
|
|
132
|
+
prisma: DbTransactionContext,
|
|
133
|
+
) {
|
|
134
|
+
super({
|
|
135
|
+
modelName: Person,
|
|
136
|
+
context: prisma,
|
|
137
|
+
include: { phones: true, address: true },
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async findByNameWithDetails(name: string): Promise<Person | null> {
|
|
142
|
+
// this.context() retorna DbTransactionContext com type safety
|
|
143
|
+
// Intellisense mostrará: person, personPhone, personAddress
|
|
144
|
+
const person = await this.context().person.findFirst({
|
|
145
|
+
where: { name },
|
|
146
|
+
include: { phones: true, address: true },
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
return person ? this.mapToDomain(person) : null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async complexOperation(): Promise<void> {
|
|
153
|
+
// Dentro de transações, this.context() retorna o client transacional
|
|
154
|
+
// Os tipos são preservados automáticamente
|
|
155
|
+
const result = await this.context().personPhone.createMany({
|
|
156
|
+
data: [
|
|
157
|
+
{ personId: 1, phone: '123456' },
|
|
158
|
+
{ personId: 1, phone: '789012' },
|
|
159
|
+
],
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Comportamento Automático com Type Safety**:
|
|
166
|
+
- **Fora de transações**: `this.context()` retorna `DbTransactionContext` com acesso tipado aos models
|
|
167
|
+
- **Dentro de transações**: `this.context()` retorna o cliente transacional preservando os tipos
|
|
168
|
+
- **Intellisense**: Com os 3 type parameters, o IDE autocompleta todos os models disponíveis
|
|
169
|
+
|
|
170
|
+
Isso garante que suas queries sempre executem no contexto correto com segurança de tipos.
|
|
171
|
+
|
|
172
|
+
### Método `remove()` com Orphan Removal
|
|
173
|
+
|
|
174
|
+
O método `remove()` herdado de `RepositoryBase` possui internamente uma função de `orphanRemoval` que remove automaticamente todas as entidades associadas (relacionamentos) quando a entidade principal é deletada.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Exemplo: Deletar uma Pessoa
|
|
178
|
+
await this.repository.delete(personId)
|
|
179
|
+
|
|
180
|
+
// Internamente, o RepositoryBase.remove() executará:
|
|
181
|
+
// 1. Remove PersonPhones associados (orphanRemoval)
|
|
182
|
+
// 2. Remove PersonAddress associado (orphanRemoval)
|
|
183
|
+
// 3. Remove Person
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Para evitar deletar entidades associadas**, passe um array de relacionamentos que devem ser **preservados** como segundo parâmetro:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
@Injectable()
|
|
190
|
+
export class PersonRepository
|
|
191
|
+
extends RepositoryBase<Person, DbTransactionContext, 'person'>
|
|
192
|
+
implements IPersonRepository
|
|
193
|
+
{
|
|
194
|
+
// ... constructor ...
|
|
195
|
+
|
|
196
|
+
delete(id: number): Promise<void> {
|
|
197
|
+
// 'address' não será deletado, apenas desvínculado
|
|
198
|
+
return this.remove<Prisma.PersonWhereUniqueInput>(
|
|
199
|
+
{ id },
|
|
200
|
+
['address'] // Preservar relacionamento
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Sintaxe completa do método `remove()`**:
|
|
207
|
+
```typescript
|
|
208
|
+
protected remove<TWhere = any>(
|
|
209
|
+
where: TWhere,
|
|
210
|
+
notCascadeEntityProps?: Array<keyof TEntity>, // Relacionamentos a preservar
|
|
211
|
+
externalServices?: Promise<any> // Promises dentro da transação
|
|
212
|
+
): Promise<void>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Exemplos práticos**:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// ❌ Deleta tudo (Person, Phones, Address)
|
|
219
|
+
await this.remove({ id: 1 })
|
|
220
|
+
|
|
221
|
+
// ✅ Deleta Person e Phones, mas preserva Address
|
|
222
|
+
await this.remove({ id: 1 }, ['address'])
|
|
223
|
+
|
|
224
|
+
// ✅ Deleta Person, mas preserva Phones e Address
|
|
225
|
+
await this.remove({ id: 1 }, ['phones', 'address'])
|
|
226
|
+
|
|
227
|
+
// ✅ Deleta Person e Address, mas preserva Phones
|
|
228
|
+
await this.remove({ id: 1 }, ['phones'])
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Método `removeMany()` com Orphan Removal
|
|
232
|
+
|
|
233
|
+
Similar ao `remove()`, mas deleta múltiplas entidades em uma única operação transacional.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
@Injectable()
|
|
237
|
+
export class PersonRepository
|
|
238
|
+
extends RepositoryBase<Person, DbTransactionContext, 'person'>
|
|
239
|
+
implements IPersonRepository
|
|
240
|
+
{
|
|
241
|
+
// ... constructor ...
|
|
242
|
+
|
|
243
|
+
async deleteInactive(): Promise<void> {
|
|
244
|
+
// Deleta múltiplas pessoas inativas
|
|
245
|
+
return this.removeMany<Prisma.PersonWhereInput>(
|
|
246
|
+
{ active: false },
|
|
247
|
+
['address'] // Preservar addresses mesmo ao deletar múltiplas pessoas
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Sintaxe completa do método `removeMany()`**:
|
|
254
|
+
```typescript
|
|
255
|
+
protected removeMany<TWhere = any>(
|
|
256
|
+
where: TWhere,
|
|
257
|
+
notCascadeEntityProps?: Array<keyof TEntity>, // Relacionamentos a preservar
|
|
258
|
+
externalServices?: Promise<any> // Promises dentro da transação
|
|
259
|
+
): Promise<void>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Parâmetro `externalServices` para Transações
|
|
263
|
+
|
|
264
|
+
Ambos os métodos `remove()` e `removeMany()` possuem um terceiro parâmetro `externalServices` que aceita uma `Promise`. Essa promise é **executada dentro da transação aberta**, permitindo que você execute operações externas de forma atômica.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
@Injectable()
|
|
268
|
+
export class PersonRepository
|
|
269
|
+
extends RepositoryBase<Person, DbTransactionContext, 'person'>
|
|
270
|
+
implements IPersonRepository
|
|
271
|
+
{
|
|
272
|
+
constructor(
|
|
273
|
+
@Inject(PRISMA_TOKEN) prisma: DbTransactionContext,
|
|
274
|
+
private readonly auditService: AuditService,
|
|
275
|
+
private readonly notificationService: NotificationService,
|
|
276
|
+
) {
|
|
277
|
+
super({
|
|
278
|
+
modelName: Person,
|
|
279
|
+
context: prisma,
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async deleteWithAudit(id: number, userId: string): Promise<void> {
|
|
284
|
+
// Executar auditoria dentro da transação
|
|
285
|
+
const auditPromise = this.auditService.logDeletion(id, userId)
|
|
286
|
+
|
|
287
|
+
return this.remove<Prisma.PersonWhereUniqueInput>(
|
|
288
|
+
{ id },
|
|
289
|
+
[],
|
|
290
|
+
auditPromise // Executado dentro da transação
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async deleteInactiveWithNotification(): Promise<void> {
|
|
295
|
+
// Executar múltiplas operações externas
|
|
296
|
+
const externalOps = Promise.all([
|
|
297
|
+
this.auditService.logBulkDeletion('inactive_persons'),
|
|
298
|
+
this.notificationService.notifyAdmins('Deleted inactive persons'),
|
|
299
|
+
])
|
|
300
|
+
|
|
301
|
+
return this.removeMany<Prisma.PersonWhereInput>(
|
|
302
|
+
{ active: false },
|
|
303
|
+
['address'],
|
|
304
|
+
externalOps // Ambas as operações dentro da transação
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Comportamento do `externalServices`**:
|
|
311
|
+
- A promise é executada **antes** do delete/deleteMany acontecer
|
|
312
|
+
- Se a promise falhar, a transação inteira é revertida
|
|
313
|
+
- Todas as operações (incluindo a promise) são executadas de forma **atômica**
|
|
314
|
+
- Se não informar `externalServices`, o delete acontece normalmente sem dependências
|
|
315
|
+
|
|
316
|
+
**Caso de Uso**:
|
|
317
|
+
Use `externalServices` para operações que devem ser garantidas atomicamente com a deleção:
|
|
318
|
+
- Auditoria de exclusões
|
|
319
|
+
- Notificações
|
|
320
|
+
- Atualização de contadores
|
|
321
|
+
- Invalidação de cache
|
|
322
|
+
- Sincronização com sistemas externos
|
|
323
|
+
|
|
324
|
+
**Caso de Uso**:
|
|
325
|
+
Use `skipOrphanRemovalOn` quando você quer transferir relacionamentos para outro registro ou manter histórico antes de deletar a entidade principal.
|
|
326
|
+
|
|
327
|
+
## Configuração Avançada
|
|
328
|
+
|
|
329
|
+
### Configurar Opções do PrismaClient (Opcional)
|
|
330
|
+
|
|
331
|
+
Se você precisar personalizar opções do PrismaClient (como adapters, logging, etc.):
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { setPrismaClientOptions } from '@koalarx/nest'
|
|
335
|
+
import type { PrismaClientOptions } from 'prisma/generated/internal/prismaNamespace'
|
|
336
|
+
|
|
337
|
+
// Configure ANTES de inicializar a aplicação
|
|
338
|
+
setPrismaClientOptions({
|
|
339
|
+
log: [{ emit: 'event', level: 'query' }],
|
|
340
|
+
// outras opções...
|
|
341
|
+
} as PrismaClientOptions)
|
|
342
|
+
|
|
343
|
+
// Depois crie seu módulo
|
|
344
|
+
const app = await NestFactory.create(AppModule)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Registrar Cliente Manualmente (Fallback)
|
|
348
|
+
|
|
349
|
+
Se por algum motivo a busca automática não encontrar o cliente, você pode registrá-lo manualmente:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { setPrismaClient } from '@koalarx/nest'
|
|
353
|
+
import { PrismaClient } from './path/to/prisma/generated/client'
|
|
354
|
+
|
|
355
|
+
// Registra manualmente como fallback
|
|
356
|
+
setPrismaClient(PrismaClient)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Query Logging
|
|
360
|
+
|
|
361
|
+
### Ativar Logs de Queries
|
|
362
|
+
|
|
363
|
+
Configure a variável de ambiente:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
PRISMA_QUERY_LOG=true
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Ou no arquivo `.env`:
|
|
370
|
+
|
|
371
|
+
```env
|
|
372
|
+
PRISMA_QUERY_LOG=true
|
|
373
|
+
NODE_ENV=development
|
|
374
|
+
DATABASE_URL=postgresql://...
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Quando ativado, todas as queries SQL executadas serão logadas no console:
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
SELECT "public"."person"."id", "public"."person"."name" FROM "public"."person" WHERE "public"."person"."id" = $1
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Resolução de Problemas
|
|
384
|
+
|
|
385
|
+
### Erro: "Cannot find module 'prisma/generated/client'"
|
|
386
|
+
|
|
387
|
+
**Causa**: O cliente Prisma não foi gerado.
|
|
388
|
+
|
|
389
|
+
**Solução**:
|
|
390
|
+
```bash
|
|
391
|
+
# 1. Verifique se tem schema.prisma
|
|
392
|
+
ls prisma/schema.prisma
|
|
393
|
+
|
|
394
|
+
# 2. Gere o cliente
|
|
395
|
+
bunx prisma generate
|
|
396
|
+
|
|
397
|
+
# 3. Confirme que a pasta foi criada
|
|
398
|
+
ls prisma/generated/
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Erro: "PrismaClient is not a constructor"
|
|
402
|
+
|
|
403
|
+
**Causa**: O módulo Prisma foi importado antes do NestJS inicializar.
|
|
404
|
+
|
|
405
|
+
**Solução**: Certifique-se de que `PrismaService` é injetado via NestJS dependency injection, não importado diretamente:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
// ❌ ERRADO
|
|
409
|
+
import { PrismaService } from '@koalarx/nest'
|
|
410
|
+
const prisma = new PrismaService()
|
|
411
|
+
|
|
412
|
+
// ✅ CORRETO
|
|
413
|
+
@Injectable()
|
|
414
|
+
export class MyService {
|
|
415
|
+
constructor(private prisma: PrismaService) {}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Erro: "PrismaService não está registrado"
|
|
420
|
+
|
|
421
|
+
**Causa**: O módulo `KoalaNestModule` não foi importado.
|
|
422
|
+
|
|
423
|
+
**Solução**: Importe o módulo principal:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { KoalaNestModule } from '@koalarx/nest'
|
|
427
|
+
import { Module } from '@nestjs/common'
|
|
428
|
+
|
|
429
|
+
@Module({
|
|
430
|
+
imports: [
|
|
431
|
+
KoalaNestModule.register({
|
|
432
|
+
env: yourEnvSchema,
|
|
433
|
+
// ...
|
|
434
|
+
}),
|
|
435
|
+
],
|
|
436
|
+
})
|
|
437
|
+
export class AppModule {}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Prisma Client não encontra a pasta gerada
|
|
441
|
+
|
|
442
|
+
Se a busca automática não encontrar seu cliente (por exemplo, em monorepos complexos):
|
|
443
|
+
|
|
444
|
+
1. Verifique o caminho exato onde o cliente foi gerado
|
|
445
|
+
2. Use `setPrismaClient()` com o caminho explícito
|
|
446
|
+
3. Ou configure `PRISMA_GENERATED_PATH` se a lib suportar
|
|
447
|
+
|
|
448
|
+
## Arquitetura Interna
|
|
449
|
+
|
|
450
|
+
### PrismaService e Proxy Transparente
|
|
451
|
+
|
|
452
|
+
`PrismaService` usa um `Proxy` JavaScript para fornecer acesso transparente:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// Internamente
|
|
456
|
+
private prismaInstance: PrismaClient
|
|
457
|
+
|
|
458
|
+
// O Proxy permite:
|
|
459
|
+
this.prisma.person.findMany() // ✅ Redireciona para prismaInstance
|
|
460
|
+
this.prisma.$transaction(fn) // ✅ Preserva métodos especiais
|
|
461
|
+
this.prisma.withTransaction(fn) // ✅ Métodos adicionados pela lib
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Busca de Resolução
|
|
465
|
+
|
|
466
|
+
O processo de descoberta (`prisma-resolver.ts`) funciona em etapas:
|
|
467
|
+
|
|
468
|
+
1. Tenta `process.cwd()` + `prisma/generated/client`
|
|
469
|
+
2. Tenta diretório do arquivo main
|
|
470
|
+
3. Tenta caminhos relativos diversos
|
|
471
|
+
4. Se nada funcionar, checa se foi registrado manualmente via `setPrismaClient()`
|
|
472
|
+
|
|
473
|
+
## Exemplo Completo
|
|
474
|
+
|
|
475
|
+
Veja a pasta `apps/example/` no repositório para um exemplo completo integrando:
|
|
476
|
+
|
|
477
|
+
- ✅ Prisma com schema definido
|
|
478
|
+
- ✅ Repositórios usando `RepositoryBase`
|
|
479
|
+
- ✅ Serviços injetando `PrismaService`
|
|
480
|
+
- ✅ Transações com `withTransaction`
|
|
481
|
+
- ✅ Query logging ativado
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
cd apps/example
|
|
485
|
+
bunx prisma generate
|
|
486
|
+
bun run start:debug
|
|
487
|
+
```
|