@alevnyacow/nzmt 0.15.12 → 0.15.14
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 +63 -30
- package/bin/cli.js +7 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,9 @@ Next Zod Modules Toolkit. Next.js tools you actually missed + a scaffolder for s
|
|
|
17
17
|
|
|
18
18
|
# Quick start with Prisma
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Assuming you have a Next.js project with a generated Prisma client, and configured `@tanstack/react-query`:
|
|
21
|
+
|
|
22
|
+
## Setup phase
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
25
|
# 1. Install NZMT and dependencies
|
|
@@ -31,15 +33,21 @@ npm i inversify zod reflect-metadata @alevnyacow/nzmt
|
|
|
31
33
|
# }
|
|
32
34
|
# }
|
|
33
35
|
|
|
34
|
-
# 3. Initialize NZMT
|
|
35
|
-
npx nzmt init prismaClientPath:@/
|
|
36
|
-
|
|
36
|
+
# 3. Initialize NZMT with the absolute Prisma client path as a parameter
|
|
37
|
+
npx nzmt init prismaClientPath:@/generated/prisma/client
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After NZMT initialization some basic infrastructure and config file were scaffolded. Open scaffolded file `/server/infrastructure/prisma/client.ts`, then import and set up there the necessary Prisma adapter. Now you’re ready to use NZMT!
|
|
41
|
+
|
|
42
|
+
## Example 1. CRUD for `User` entity with API route handlers and react queries
|
|
43
|
+
|
|
44
|
+
Assuming you have `User` prisma schema.
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
```bash
|
|
39
47
|
npx nzmt crud-api user
|
|
40
48
|
```
|
|
41
49
|
|
|
42
|
-
Let's break down what's been scaffolded after
|
|
50
|
+
Let's break down what's been scaffolded after this command:
|
|
43
51
|
|
|
44
52
|
### 1. Scaffolded `UserEntity`
|
|
45
53
|
|
|
@@ -70,7 +78,7 @@ export class User {
|
|
|
70
78
|
}
|
|
71
79
|
```
|
|
72
80
|
|
|
73
|
-
All that’s left is to define the entity’s structure and validation in the static field `schema` (Zod). All related types are already derived, so contracts update automatically. Every scaffolded source file is fully editable
|
|
81
|
+
All that’s left is to define the entity’s structure and validation in the static field `schema` (Zod). All related types are already derived (including ones in client-side queries), so contracts update automatically. **Every scaffolded source file is fully editable**, so you're in full control - you can add fields, methods, etc..
|
|
74
82
|
|
|
75
83
|
### 2. Scaffolded `UserStore` contract with `RAM` (in-memory) and `Prisma` implementations
|
|
76
84
|
|
|
@@ -141,26 +149,7 @@ const mappers = {
|
|
|
141
149
|
|
|
142
150
|
};
|
|
143
151
|
},
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
},
|
|
149
|
-
toDetails: (source: Prisma.UserGetPayload<{ include: { } }>): Types['details'] => {
|
|
150
|
-
return {
|
|
151
|
-
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
toCreatePayload: (source: Types['createPayload']): Prisma.UserCreateInput => {
|
|
155
|
-
return {
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
},
|
|
159
|
-
toUpdatePayload: (source: Types['updatePayload']): Prisma.UserUpdateInput => {
|
|
160
|
-
return {
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
}
|
|
152
|
+
... few other mappers
|
|
164
153
|
}
|
|
165
154
|
|
|
166
155
|
@injectable()
|
|
@@ -197,7 +186,7 @@ type Method = UserAPI['endpoints']['GET']
|
|
|
197
186
|
|
|
198
187
|
const endpoint = '/api/user-controller'
|
|
199
188
|
|
|
200
|
-
export const
|
|
189
|
+
export const useUserAPI_GET = (payload: Method['payload']) => {
|
|
201
190
|
return useQuery<Method['response'], Method['error']>({
|
|
202
191
|
queryKey: [endpoint, payload],
|
|
203
192
|
queryFn: () => apiRequest(endpoint, 'GET')(payload)
|
|
@@ -208,6 +197,50 @@ export const useUserController_GET = (payload: Method['payload']) => {
|
|
|
208
197
|
- Fully typed and ready for client-side use.
|
|
209
198
|
- `apiRequest` handles endpoint, method, and payload conveniently (also scaffolded and editable).
|
|
210
199
|
|
|
211
|
-
**
|
|
200
|
+
And once again - **all code is editable - you stay in full control! 🔨⚙️**
|
|
201
|
+
|
|
202
|
+
Now you can start building your domain logic — NZMT handles the boilerplate for you. 🪄
|
|
203
|
+
|
|
204
|
+
## Example 2. CRUD for `Product` entity with only server actions
|
|
205
|
+
|
|
206
|
+
Assuming you have `Product` prisma schema.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npx nzmt crud-service product
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Command `crud-service` is a lot like the `crud-api` command, but it stops after generating service. So, you need to:
|
|
213
|
+
|
|
214
|
+
- describe `Product` entity (`/shared/entities/product/product.entity.ts`)
|
|
215
|
+
- tweak the Product store schemas if needed (`/server/stores/product/product.store.ts`)
|
|
216
|
+
- write Prisma store mappers (`/server/stores/product/product.store.prisma.ts`) as in previous example.
|
|
217
|
+
|
|
218
|
+
`Services` can be used in Server Actions. So, when you make `crud-api`, this generated service can also be used in Server Actions. Using it is very simple, you need `fromDI` function, which was scaffolded when you initialized NMZT. Let's take a look at combined example with two services we've just created:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
'use server'
|
|
212
222
|
|
|
213
|
-
|
|
223
|
+
import { fromDI } from "@/server/di"
|
|
224
|
+
import type { UserService } from "@/server/services/user"
|
|
225
|
+
import type { ProductService } from "@/server/services/product"
|
|
226
|
+
|
|
227
|
+
export default async function() {
|
|
228
|
+
// keys in fromDI function are strongly typed
|
|
229
|
+
const userService = fromDI<UserService>('UserService')
|
|
230
|
+
const productService = fromDI<ProductService>('ProductService')
|
|
231
|
+
|
|
232
|
+
const driver8 = await userService.getDetails({
|
|
233
|
+
filter: { id: 'driver-8' }
|
|
234
|
+
})
|
|
235
|
+
const allProducts = await productService.getList({
|
|
236
|
+
filter: { }
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
return <div>
|
|
240
|
+
Take a break, {JSON.stringify(driver8)}
|
|
241
|
+
{JSON.stringify(driver8)}, take a break
|
|
242
|
+
|
|
243
|
+
Also we've got some products: {JSON.stringify(allProducts)}
|
|
244
|
+
</div>
|
|
245
|
+
}
|
|
246
|
+
```
|
package/bin/cli.js
CHANGED
|
@@ -322,12 +322,11 @@ function initPrisma() {
|
|
|
322
322
|
fs.mkdirSync(prismaFolder, { recursive: true })
|
|
323
323
|
|
|
324
324
|
fs.writeFileSync(path.resolve(prismaFolder, 'client.ts'), [
|
|
325
|
-
|
|
325
|
+
'/** ! import required Prisma adapter */',
|
|
326
326
|
`import { PrismaClient } from '${prismaClientPath}'`,
|
|
327
327
|
``,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
`})`,
|
|
328
|
+
'const connectionString = `${process.env.DATABASE_URL}`',
|
|
329
|
+
`const adapter = /** ! instanse of the adapter */`,
|
|
331
330
|
``,
|
|
332
331
|
`export const prismaClient = new PrismaClient({ adapter })`
|
|
333
332
|
].join('\n'))
|
|
@@ -1105,7 +1104,7 @@ function generateQueries(lowerCase, upperCase) {
|
|
|
1105
1104
|
``,
|
|
1106
1105
|
rootMethod === 'GET'
|
|
1107
1106
|
? [
|
|
1108
|
-
`export const use${upperCase}
|
|
1107
|
+
`export const use${upperCase}API_${rootMethod} = (payload: Method['payload']) => {`,
|
|
1109
1108
|
`\treturn useQuery<Method['response'], Method['error']>({`,
|
|
1110
1109
|
`\t\tqueryKey: [endpoint, payload],`,
|
|
1111
1110
|
`\t\tqueryFn: () => apiRequest(endpoint, 'GET')(payload)`,
|
|
@@ -1113,7 +1112,7 @@ function generateQueries(lowerCase, upperCase) {
|
|
|
1113
1112
|
`}`
|
|
1114
1113
|
].join('\n')
|
|
1115
1114
|
: [
|
|
1116
|
-
`export const use${upperCase}
|
|
1115
|
+
`export const use${upperCase}API_${rootMethod} = () => {`,
|
|
1117
1116
|
`\treturn useMutation<Method['response'], Method['error'], Method['payload']>({`,
|
|
1118
1117
|
`\t\tmutationFn: apiRequest(endpoint, '${rootMethod}')`,
|
|
1119
1118
|
`\t})`,
|
|
@@ -1141,7 +1140,7 @@ function generateQueries(lowerCase, upperCase) {
|
|
|
1141
1140
|
``,
|
|
1142
1141
|
method === 'GET'
|
|
1143
1142
|
? [
|
|
1144
|
-
`export const use${upperCase}
|
|
1143
|
+
`export const use${upperCase}API_${fullMethodName} = (payload: Method['payload']) => {`,
|
|
1145
1144
|
`\treturn useQuery<Method['response'], Method['error']>({`,
|
|
1146
1145
|
`\t\tqueryKey: [endpoint, payload],`,
|
|
1147
1146
|
`\t\tqueryFn: () => apiRequest(endpoint, 'GET')(payload)`,
|
|
@@ -1149,7 +1148,7 @@ function generateQueries(lowerCase, upperCase) {
|
|
|
1149
1148
|
`}`
|
|
1150
1149
|
].join('\n')
|
|
1151
1150
|
: [
|
|
1152
|
-
`export const use${upperCase}
|
|
1151
|
+
`export const use${upperCase}API_${fullMethodName} = () => {`,
|
|
1153
1152
|
`\treturn useMutation<Method['response'], Method['error'], Method['payload']>({`,
|
|
1154
1153
|
`\t\tmutationFn: apiRequest(endpoint, '${method}')`,
|
|
1155
1154
|
`\t})`,
|