@prismakit/cli 3.2.0 → 3.2.2
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/package.json +11 -11
- package/skills/prismakit/SKILL.md +18 -12
- package/skills/prismakit/examples.md +3 -3
- package/skills/prismakit/reference.md +15 -7
- package/skills/prismakit-nestjs/SKILL.md +42 -15
- package/skills/prismakit-nestjs/examples.md +27 -15
- package/skills/prismakit-nestjs/reference.md +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prismakit/cli",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "CLI for PrismaKit — generate modules, validate compose, install agent skills",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -28,8 +28,15 @@
|
|
|
28
28
|
"LICENSE",
|
|
29
29
|
"README.md"
|
|
30
30
|
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"clean": "rm -rf dist"
|
|
37
|
+
},
|
|
31
38
|
"dependencies": {
|
|
32
|
-
"@prismakit/core": "
|
|
39
|
+
"@prismakit/core": "workspace:*"
|
|
33
40
|
},
|
|
34
41
|
"peerDependencies": {
|
|
35
42
|
"@prismakit/core": ">=3.2.0 <4"
|
|
@@ -56,12 +63,5 @@
|
|
|
56
63
|
"prismakit",
|
|
57
64
|
"cursor",
|
|
58
65
|
"skills"
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
"build": "tsup",
|
|
62
|
-
"test": "vitest run",
|
|
63
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
64
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
65
|
-
"clean": "rm -rf dist"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -32,7 +32,7 @@ Violations are bugs. Enforce with `@prismakit/eslint-plugin` + this skill.
|
|
|
32
32
|
| Reads/writes | `*Repository` from `createRepository` / Nest factories |
|
|
33
33
|
| Tx writes | `invalidate: 'none'` then `invalidateCache` after commit |
|
|
34
34
|
| User-facing reads | `setCache: true` when the repo has cache config |
|
|
35
|
-
| Relations in `select` | `model` + `scalarFields`
|
|
35
|
+
| Relations in `select` | `model` + Prisma meta loaded (or `scalarFields` when meta unavailable) |
|
|
36
36
|
| ESLint | `prismakit.configs.recommended` |
|
|
37
37
|
|
|
38
38
|
## Layers
|
|
@@ -64,7 +64,7 @@ Helpers may inject repositories — never the Prisma client.
|
|
|
64
64
|
## Create a repository (core)
|
|
65
65
|
|
|
66
66
|
```typescript
|
|
67
|
-
import {
|
|
67
|
+
import { PrismaClient } from '@prisma/client';
|
|
68
68
|
import { createRepository } from '@prismakit/core';
|
|
69
69
|
import { RedisCacheAdapter } from '@prismakit/redis';
|
|
70
70
|
|
|
@@ -72,9 +72,8 @@ const DAY = 86_400;
|
|
|
72
72
|
|
|
73
73
|
const UserRepoClass = createRepository({
|
|
74
74
|
model: 'user',
|
|
75
|
-
scalarFields: Prisma.UserScalarFieldEnum,
|
|
76
75
|
cache: { ttl: DAY, nullTtl: 60, sensitiveFields: ['password'] },
|
|
77
|
-
lock: true, // table + columns from Prisma meta
|
|
76
|
+
lock: true, // table + columns resolved from Prisma schema meta
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
const prisma = new PrismaClient();
|
|
@@ -82,16 +81,18 @@ const cache = new RedisCacheAdapter({ prefix: 'myapp' });
|
|
|
82
81
|
export const users = new UserRepoClass({ prisma, cache });
|
|
83
82
|
```
|
|
84
83
|
|
|
85
|
-
`defineRepository`
|
|
84
|
+
`defineRepository` is an alias of `createRepository` in core. Note: `createPrismaRepository` is also an alias in core, but in `@prismakit/nestjs` it aliases `createInjectableRepository` instead — avoid using it to prevent confusion.
|
|
85
|
+
|
|
86
|
+
**NestJS apps:** use `createDefineRepo` / `defineAppRepo` with app-wide cache defaults instead — see skill `prismakit-nestjs`.
|
|
86
87
|
|
|
87
88
|
| Option | Description |
|
|
88
89
|
|--------|-------------|
|
|
89
90
|
| `model` | Prisma client key (`prisma.user` → `'user'`). Needed for cache + compose. |
|
|
90
|
-
| `scalarFields` | Usually `Prisma.XScalarFieldEnum`.
|
|
91
|
-
| `cache` | `CacheOptions` or `true`
|
|
91
|
+
| `scalarFields` | Usually `Prisma.XScalarFieldEnum`. **Optional** when `schemaPath` / DMMF meta is loaded (default since 3.1). |
|
|
92
|
+
| `cache` | `CacheOptions` or `true` (uses app defaults when bound via `createDefineRepo`). |
|
|
92
93
|
| `lock` | `true` / client key / `@@map` table / `{ tableName, columns }`. |
|
|
93
94
|
| `primaryKey` | Override only. Defaults to schema `@id` / `@@id` (composite `string[]`) or `id`. |
|
|
94
|
-
| `schemaPath` | Path to `schema.prisma` when meta is not loaded globally. |
|
|
95
|
+
| `schemaPath` | Path to `schema.prisma` when meta is not loaded globally. Default: `prisma/schema.prisma`. |
|
|
95
96
|
| `getDelegate` | Optional. Defaults to `(c) => c[model]`. |
|
|
96
97
|
|
|
97
98
|
Put files under `**/repositories/**`. Keep Prisma client construction under `**/infrastructure/prisma/**`.
|
|
@@ -122,7 +123,8 @@ await users.getFirst({
|
|
|
122
123
|
| `getMany` | array (`take` / `skip` / `orderBy`) |
|
|
123
124
|
| `getManyPaginate` | `{ data, meta: { page, pageSize, totalItems, totalPages } }` |
|
|
124
125
|
| `getManyCursor` | `{ data, nextCursor, hasMore }` |
|
|
125
|
-
| `
|
|
126
|
+
| `getThrowFirst` | first match; throws if missing |
|
|
127
|
+
| `count` / `exists` | `number` / `boolean` |
|
|
126
128
|
| `aggregate` / `groupBy` | Prisma delegate results |
|
|
127
129
|
|
|
128
130
|
`id` is `string` or `Record<string, string>` for composite PKs (object form for `@@id([a,b])`).
|
|
@@ -133,8 +135,11 @@ await users.getFirst({
|
|
|
133
135
|
|
|
134
136
|
| Method | Default `invalidate` |
|
|
135
137
|
|--------|----------------------|
|
|
136
|
-
| `create` / `createMany` | `queries` |
|
|
137
|
-
| `updateById` / `
|
|
138
|
+
| `create` / `createMany` / `createManyAndReturn` | `queries` |
|
|
139
|
+
| `updateById` / `update` / `updateMany` / `updateManyAndReturn` | `all` |
|
|
140
|
+
| `upsert` / `upsertMany` | `all` |
|
|
141
|
+
| `deleteById` / `delete` / `deleteMany` | `all` |
|
|
142
|
+
| `queryRaw` / `executeRaw` | none (raw SQL — no cache) |
|
|
138
143
|
|
|
139
144
|
```typescript
|
|
140
145
|
await users.updateById({
|
|
@@ -239,8 +244,9 @@ Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules
|
|
|
239
244
|
|
|
240
245
|
## Observability
|
|
241
246
|
|
|
242
|
-
- Core: `setTelemetry({ enabled, onEvent })` or Nest `telemetry` / `queryLog.slowThreshold`.
|
|
247
|
+
- Core: `setTelemetry({ enabled, onEvent, slowThreshold })` or Nest `telemetry` / `queryLog.slowThreshold`.
|
|
243
248
|
- Optional: `@prismakit/opentelemetry` → `createPrismaKitTelemetry({ slowThreshold })`.
|
|
249
|
+
- Events: `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` / `cache.error`, `compose.*`, `lock.*`, `stampede.*`, `query.complete` / `query.slow`.
|
|
244
250
|
|
|
245
251
|
## Clean code
|
|
246
252
|
|
|
@@ -6,7 +6,7 @@ Copy-paste patterns for `@prismakit/core`. Contract: [SKILL.md](SKILL.md).
|
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
// src/modules/users/repositories/user.repository.ts
|
|
9
|
-
import { Prisma } from '@prisma/client';
|
|
9
|
+
import { Prisma } from '@prisma/client'; // or generated client path for Prisma 7
|
|
10
10
|
import { createRepository } from '@prismakit/core';
|
|
11
11
|
|
|
12
12
|
const DAY = 86_400;
|
|
@@ -23,7 +23,7 @@ export const userSelectPresets = {
|
|
|
23
23
|
|
|
24
24
|
export const UserRepoClass = createRepository({
|
|
25
25
|
model: 'user',
|
|
26
|
-
scalarFields:
|
|
26
|
+
// scalarFields: optional when schemaPath/DMMF meta is loaded (default since 3.1)
|
|
27
27
|
cache: {
|
|
28
28
|
ttl: DAY,
|
|
29
29
|
nullTtl: 60,
|
|
@@ -40,7 +40,7 @@ export type UserRepository = InstanceType<typeof UserRepoClass>;
|
|
|
40
40
|
|
|
41
41
|
```typescript
|
|
42
42
|
// src/infrastructure/prisma/repos.ts
|
|
43
|
-
import { PrismaClient } from '@prisma/client';
|
|
43
|
+
import { PrismaClient } from '@prisma/client'; // or generated client for Prisma 7
|
|
44
44
|
import { RedisCacheAdapter } from '@prismakit/redis';
|
|
45
45
|
import { UserRepoClass } from '../../modules/users/repositories/user.repository';
|
|
46
46
|
|
|
@@ -29,7 +29,7 @@ pnpm add -D @prismakit/eslint-plugin @prismakit/cli
|
|
|
29
29
|
createRepository(options) → new RepoClass(deps: RepositoryDeps)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Alias: `defineRepository`. (`createPrismaRepository` is also an alias in core but maps to `createInjectableRepository` in `@prismakit/nestjs` — avoid it to prevent confusion.)
|
|
33
33
|
|
|
34
34
|
`RepositoryDeps`: `{ prisma, cache?, registry?, autoCompose? }`.
|
|
35
35
|
|
|
@@ -58,12 +58,13 @@ All methods accept optional `tx`. Cached repos also accept cache fields (see bel
|
|
|
58
58
|
|--------|------------|---------|
|
|
59
59
|
| `getById` | `id`, `select?`, `lock?`, `setCache?` | `T \| null` |
|
|
60
60
|
| `getThrowById` | same | `T` (throws if missing) |
|
|
61
|
-
| `getFirst` | `where?`, `select?`, `lock?`, `setCache?`, `cacheTags?` | `T \| null` |
|
|
61
|
+
| `getFirst` | `where?`, `select?`, `lock?`, `setCache?`, `cacheTags?`, `orderBy?` | `T \| null` |
|
|
62
|
+
| `getThrowFirst` | same as `getFirst` | `T` (throws if missing) |
|
|
62
63
|
| `getMany` | `where?`, `select?`, `orderBy?`, `take?`, `skip?`, `lock?`, `setCache?`, `cacheTags?` | `T[]` |
|
|
63
64
|
| `getManyPaginate` | `where?`, `select?`, `orderBy?`, `page?`, `pageSize?`, `setCache?`, `cacheTags?` | `PaginatedResult<T>` |
|
|
64
65
|
| `getManyCursor` | `where?`, `select?`, `orderBy?`, `cursor?`, `take?`, `skip?`, `setCache?`, `cacheTags?` | `CursorPage<T>`; with `cursor`, default `skip: 1` |
|
|
65
|
-
| `count` | `where?`, `
|
|
66
|
-
| `exists` | `where?`, `setCache?`, `cacheTags?` | `
|
|
66
|
+
| `count` | `where?`, `setCache?`, `cacheTags?` | `number` |
|
|
67
|
+
| `exists` | `where?`, `setCache?`, `cacheTags?` | `boolean` |
|
|
67
68
|
| `aggregate` | Prisma aggregate args + `setCache?`, `cacheTags?` | delegate result |
|
|
68
69
|
| `groupBy` | Prisma groupBy args + `setCache?`, `cacheTags?` | delegate result |
|
|
69
70
|
|
|
@@ -86,6 +87,13 @@ All methods accept optional `tx`. Cached repos also accept cache fields (see bel
|
|
|
86
87
|
| `upsert` | `where`, `create`, `update`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
|
|
87
88
|
| `deleteById` | `id`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
|
|
88
89
|
| `deleteMany` | `where`, `invalidate?`, `tags?` | `all` | `{ count }` |
|
|
90
|
+
| `update` | `where`, `data`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
|
|
91
|
+
| `delete` | `where`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
|
|
92
|
+
| `createManyAndReturn` | `data[]`, `select?`, `skipDuplicates?`, `invalidate?`, `tags?` | `queries` | `T[]` |
|
|
93
|
+
| `updateManyAndReturn` | `where`, `data`, `select?`, `invalidate?`, `tags?` | `all` | `T[]` |
|
|
94
|
+
| `upsertMany` | `data[]`, `skipDuplicates?`, `invalidate?`, `tags?` | `all` | `{ count }` |
|
|
95
|
+
| `queryRaw` | `sql`, `...params` | — | raw result |
|
|
96
|
+
| `executeRaw` | `sql`, `...params` | — | `number` (affected rows) |
|
|
89
97
|
|
|
90
98
|
Mutation `tags`: `string[] | null | undefined | ((result) => string[] | null | undefined)`.
|
|
91
99
|
|
|
@@ -112,7 +120,7 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
|
|
|
112
120
|
| `ttl` | `86400` | Entity TTL (seconds). |
|
|
113
121
|
| `nullTtl` | — | Negative cache for null results. |
|
|
114
122
|
| `sensitiveFields` | `['password']` | Selects containing these never cache. |
|
|
115
|
-
| `methods` | — | Per-method `{ enabled?, ttl? }` for `getById`, `getThrowById`, `getFirst`, `getMany`, `getManyPaginate`. |
|
|
123
|
+
| `methods` | — | Per-method `{ enabled?, ttl? }` for `getById`, `getThrowById`, `getFirst`, `getThrowFirst`, `getMany`, `getManyPaginate`, `getManyCursor`, `count`, `exists`, `aggregate`, `groupBy`. |
|
|
116
124
|
| `defaultSetCache` | `false` | Reads cache unless caller passes `setCache: false`. |
|
|
117
125
|
| `stampede` | see below | Per-repo stampede overrides. |
|
|
118
126
|
| `compression` | — | Hint for adapters (`'none' \| 'zstd' \| 'lz4'`). Redis adapter uses `'none' \| 'gzip'`. |
|
|
@@ -237,12 +245,13 @@ import { setTelemetry } from '@prismakit/core';
|
|
|
237
245
|
setTelemetry({
|
|
238
246
|
enabled: true,
|
|
239
247
|
onEvent: (event) => { /* metrics */ },
|
|
248
|
+
slowThreshold: 500, // emits query.slow for queries ≥ this ms
|
|
240
249
|
});
|
|
241
250
|
```
|
|
242
251
|
|
|
243
252
|
| Type | When |
|
|
244
253
|
|------|------|
|
|
245
|
-
| `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` | Cache-aside path |
|
|
254
|
+
| `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` / `cache.error` | Cache-aside path |
|
|
246
255
|
| `compose.start` / `compose.complete` | Auto-compose (`queryCount`, `durationMs`) |
|
|
247
256
|
| `lock.acquired` / `lock.waited` / `lock.timeout` | Row locks |
|
|
248
257
|
| `stampede.locked` / `stampede.waited` / `stampede.fallthrough` | Stampede protection |
|
|
@@ -312,7 +321,6 @@ const autoCompose = new AutoComposer(registry);
|
|
|
312
321
|
|
|
313
322
|
const UserRepo = createRepository({
|
|
314
323
|
model: 'user',
|
|
315
|
-
scalarFields: Prisma.UserScalarFieldEnum,
|
|
316
324
|
cache: { ttl: 86_400 },
|
|
317
325
|
});
|
|
318
326
|
const users = new UserRepo({ prisma, cache, registry, autoCompose });
|
|
@@ -23,7 +23,6 @@ pnpm add -D @prismakit/eslint-plugin @prismakit/cli
|
|
|
23
23
|
import { Module } from '@nestjs/common';
|
|
24
24
|
import { PrismaKitModule } from '@prismakit/nestjs';
|
|
25
25
|
import { RedisCacheAdapter } from '@prismakit/redis';
|
|
26
|
-
import { Prisma } from '@prisma/client';
|
|
27
26
|
|
|
28
27
|
@Module({
|
|
29
28
|
imports: [
|
|
@@ -45,37 +44,65 @@ export class AppModule {}
|
|
|
45
44
|
|
|
46
45
|
## Factory (one default)
|
|
47
46
|
|
|
48
|
-
Bind `Prisma.TypeMap` once, then define repos with
|
|
47
|
+
Bind `Prisma.TypeMap` once with app-wide cache defaults, then define repos with per-model overrides only.
|
|
49
48
|
|
|
50
49
|
```typescript
|
|
51
|
-
// src/infrastructure/prisma/define-repo.ts
|
|
50
|
+
// src/infrastructure/prisma/define-app-repo.ts
|
|
52
51
|
import { createDefineRepo } from '@prismakit/nestjs';
|
|
53
|
-
import type { Prisma } from '@prisma/client';
|
|
52
|
+
import type { Prisma } from '@prisma/client'; // or generated client path
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
const DAY = 86_400;
|
|
55
|
+
|
|
56
|
+
export const defineAppRepo = createDefineRepo<Prisma.TypeMap>({
|
|
57
|
+
cache: {
|
|
58
|
+
ttl: DAY,
|
|
59
|
+
nullTtl: 60,
|
|
60
|
+
defaultSetCache: true,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
56
63
|
```
|
|
57
64
|
|
|
58
65
|
```typescript
|
|
59
66
|
// src/modules/users/repositories/user.repository.ts
|
|
60
|
-
import {
|
|
61
|
-
import { defineRepo } from '../../../infrastructure/prisma/define-repo';
|
|
62
|
-
|
|
63
|
-
const DAY = 86_400;
|
|
67
|
+
import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
|
|
64
68
|
|
|
65
|
-
export class UserRepository extends
|
|
69
|
+
export class UserRepository extends defineAppRepo({
|
|
66
70
|
model: 'user',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
cache: {
|
|
72
|
+
defaultSetCache: false, // auth lookups pass setCache explicitly
|
|
73
|
+
sensitiveFields: ['password'],
|
|
74
|
+
methods: { getFirst: { enabled: false } },
|
|
75
|
+
},
|
|
70
76
|
}) {}
|
|
71
77
|
```
|
|
72
78
|
|
|
79
|
+
```typescript
|
|
80
|
+
// src/modules/category/repositories/category.repository.ts
|
|
81
|
+
import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
|
|
82
|
+
|
|
83
|
+
export class CategoryRepository extends defineAppRepo({
|
|
84
|
+
model: 'category',
|
|
85
|
+
cache: true, // inherits app-wide defaults (ttl, nullTtl, defaultSetCache)
|
|
86
|
+
}) {}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Uncached repo — TypeScript omits setCache / invalidateCache
|
|
91
|
+
import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
|
|
92
|
+
|
|
93
|
+
export class AuditLogRepository extends defineAppRepo({
|
|
94
|
+
model: 'auditLog',
|
|
95
|
+
}) {}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`scalarFields` is **optional** when `schemaPath` or DMMF meta is loaded (default since 3.1). Omit it in new repos.
|
|
99
|
+
|
|
73
100
|
Escape hatches (do not use as the app default):
|
|
74
101
|
|
|
75
102
|
- `defineInjectableRepository` from `@prismakit/nestjs` (package alias `defineRepository`) — phantoms + payload HKT when TypeMap is unavailable.
|
|
76
103
|
- `createInjectableRepository` without a types bag — thin, results are `unknown`. Alias: `createPrismaRepository`.
|
|
77
104
|
|
|
78
|
-
Do not import `defineRepo` from `@prismakit/nestjs` in apps that already bind `createDefineRepo` as `
|
|
105
|
+
Do not import `defineRepo` from `@prismakit/nestjs` in apps that already bind `createDefineRepo` as `defineAppRepo`.
|
|
79
106
|
|
|
80
107
|
## Register and inject
|
|
81
108
|
|
|
@@ -216,7 +243,7 @@ Reference app: [starter-prismakit-nestjs](https://github.com/fikiap23/starter-pr
|
|
|
216
243
|
|
|
217
244
|
## Clean code (Nest)
|
|
218
245
|
|
|
219
|
-
- One `
|
|
246
|
+
- One `defineAppRepo` binder under `src/infrastructure/prisma/`. Do not call `createDefineRepo` per feature.
|
|
220
247
|
- Feature modules own their repository providers; do not make every repo global.
|
|
221
248
|
- Controllers stay HTTP-only: map DTO → service method. No repository calls in controllers.
|
|
222
249
|
- Select presets live next to the repository (`minimal` / `general` / `withPassword`).
|
|
@@ -47,24 +47,30 @@ export class AppModule {}
|
|
|
47
47
|
|
|
48
48
|
Prisma 5/6: pass `dmmf: Prisma.dmmf` instead of (or in addition to skipping) `schemaPath`. Prisma 7: `schemaPath` only.
|
|
49
49
|
|
|
50
|
-
## 2. TypeMap binder
|
|
50
|
+
## 2. TypeMap binder with app-wide cache defaults
|
|
51
51
|
|
|
52
52
|
```typescript
|
|
53
|
-
// src/infrastructure/prisma/define-repo.ts
|
|
53
|
+
// src/infrastructure/prisma/define-app-repo.ts
|
|
54
54
|
import { createDefineRepo } from '@prismakit/nestjs';
|
|
55
|
-
import type { Prisma } from '
|
|
55
|
+
import type { Prisma } from 'src/infrastructure/prisma/prisma-client';
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const DAY = 86_400;
|
|
58
|
+
|
|
59
|
+
export const defineAppRepo = createDefineRepo<Prisma.TypeMap>({
|
|
60
|
+
cache: {
|
|
61
|
+
ttl: DAY,
|
|
62
|
+
nullTtl: 60,
|
|
63
|
+
defaultSetCache: true,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
58
66
|
```
|
|
59
67
|
|
|
60
68
|
## 3. Feature repository + select presets
|
|
61
69
|
|
|
62
70
|
```typescript
|
|
63
71
|
// src/modules/users/repositories/user.repository.ts
|
|
64
|
-
import { Prisma } from '
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
const DAY = 86_400;
|
|
72
|
+
import { Prisma } from 'src/infrastructure/prisma/prisma-client';
|
|
73
|
+
import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
|
|
68
74
|
|
|
69
75
|
export const userSelectPresets = {
|
|
70
76
|
minimal: { id: true } satisfies Prisma.UserSelect,
|
|
@@ -80,18 +86,24 @@ export const userSelectPresets = {
|
|
|
80
86
|
} satisfies Prisma.UserSelect,
|
|
81
87
|
};
|
|
82
88
|
|
|
83
|
-
export
|
|
89
|
+
export class UserRepository extends defineAppRepo({
|
|
84
90
|
model: 'user',
|
|
85
|
-
scalarFields: Prisma.UserScalarFieldEnum,
|
|
86
91
|
cache: {
|
|
87
|
-
|
|
88
|
-
nullTtl: 60,
|
|
92
|
+
defaultSetCache: false, // auth lookups pass setCache explicitly
|
|
89
93
|
sensitiveFields: ['password'],
|
|
90
94
|
methods: { getFirst: { enabled: false } },
|
|
91
95
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
}) {}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Simple cached repo — inherits app-wide defaults
|
|
101
|
+
import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
|
|
102
|
+
|
|
103
|
+
export class CategoryRepository extends defineAppRepo({
|
|
104
|
+
model: 'category',
|
|
105
|
+
cache: true,
|
|
106
|
+
}) {}
|
|
95
107
|
```
|
|
96
108
|
|
|
97
109
|
## 4. Feature module + thin controller + service
|
|
@@ -84,11 +84,11 @@ await this.tx.execTx<User, Prisma.TransactionClient>(async (tx) => { /* ... */ }
|
|
|
84
84
|
| `defineInjectableRepository({ model, select, create, update, where, orderBy, payload, ... })` | TypeMap unavailable. Package aliases: `defineRepository`. |
|
|
85
85
|
| `createInjectableRepository({ model, ... })` | Thin / untyped. Results `unknown` unless `toPayload` is supplied. Alias: `createPrismaRepository`. |
|
|
86
86
|
|
|
87
|
-
`createDefineRepo`
|
|
87
|
+
`createDefineRepo` accepts app-wide defaults (`cache`, `schemaPath`) and per-repo options: `model`, `scalarFields?` (optional since 3.1), `primaryKey?`, `cache?` (`true` inherits app defaults), `lock?`, `schemaPath?`.
|
|
88
88
|
|
|
89
89
|
When `cache` is set, the returned API includes `setCache` / `cacheTags` / `invalidate` / `tags` / `invalidateCache`. Otherwise those fields are omitted from the type (`HasCacheFromOptions`).
|
|
90
90
|
|
|
91
|
-
`createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `createMany`, `updateMany`, `upsert`, `deleteMany`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. `primaryKey` is optional — composite `@@id` is read from schema meta.
|
|
91
|
+
`createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `create`, `createMany`, `createManyAndReturn`, `update`, `updateById`, `updateMany`, `updateManyAndReturn`, `upsert`, `upsertMany`, `delete`, `deleteById`, `deleteMany`, `getThrowFirst`, `count`, `exists`, `aggregate`, `groupBy`, `getManyCursor`, `queryRaw`, `executeRaw`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. `primaryKey` is optional — composite `@@id` is read from schema meta.
|
|
92
92
|
|
|
93
93
|
Export the instance type with interface merging so `cache` on options gates `setCache` (a same-name `type` alias collapses to `any`):
|
|
94
94
|
|
|
@@ -108,7 +108,7 @@ type Of<S> = S extends Prisma.UserSelect
|
|
|
108
108
|
|
|
109
109
|
export const UserRepository = defineInjectableRepository({
|
|
110
110
|
model: 'user',
|
|
111
|
-
scalarFields: Prisma.UserScalarFieldEnum,
|
|
111
|
+
scalarFields: Prisma.UserScalarFieldEnum, // required for this escape hatch (meta not typed)
|
|
112
112
|
select: null! as Prisma.UserSelect,
|
|
113
113
|
create: null! as Prisma.UserCreateInput,
|
|
114
114
|
update: null! as Prisma.UserUpdateInput,
|
|
@@ -135,7 +135,7 @@ Prefer importing Nest-only APIs from `@prismakit/nestjs` and core-only helpers f
|
|
|
135
135
|
src/
|
|
136
136
|
app.module.ts # PrismaKitModule.forRootAsync
|
|
137
137
|
infrastructure/prisma/
|
|
138
|
-
define-repo.ts
|
|
138
|
+
define-app-repo.ts # createDefineRepo<Prisma.TypeMap>({ cache defaults })
|
|
139
139
|
prisma.service.ts # client construction only
|
|
140
140
|
modules/<feature>/
|
|
141
141
|
<feature>.module.ts # providers: [Service, XRepository]
|