@alevnyacow/nzmt 0.16.15 β 0.16.17
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 +59 -82
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4,34 +4,32 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Scaffold full-stack modules in Next.js in seconds with Next **Zod Modules Toolkit (NZMT)**.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Get **a DDD-inspired architecture with a contract-first approach** β and Server Actions working out of the box.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**API, services, stores, entities, validation, and React Query hooks β all generated for you.** No framework. No lock-in. Just production-ready Next.js.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# π How it works
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
- initialize NZMT once
|
|
16
|
+
- run the scaffolder (e.g. `npx nzmt crud-api user`)
|
|
17
|
+
- tweak a few files
|
|
18
|
+
- get ready-to-use React Query hooks and a backend usable via Server Actions
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
- π§ Focus on your domain logic without drowning in full-blown DDD.
|
|
19
|
-
- β¨ DI, handy API controllers and a bunch of other cool things aimed at improving your DX out of the box.
|
|
20
|
-
- πͺ Services, controllers, client queries, and other programmer stuff appear at the snap of a finger.
|
|
21
|
-
|
|
22
|
-
# Quick start with Prisma
|
|
20
|
+
# π¬ Quick start with Prisma
|
|
23
21
|
|
|
24
22
|
Assuming you have a Next.js project with a generated Prisma client, and configured `@tanstack/react-query`:
|
|
25
23
|
|
|
26
|
-
## Setup
|
|
24
|
+
## βοΈ Setup
|
|
27
25
|
|
|
28
|
-
1. Install
|
|
26
|
+
### 1. Install
|
|
29
27
|
|
|
30
28
|
```bash
|
|
31
29
|
npm i inversify zod reflect-metadata @alevnyacow/nzmt
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
2. Enable decorators in tsconfig.json
|
|
32
|
+
### 2. Enable decorators in tsconfig.json
|
|
35
33
|
|
|
36
34
|
```ts
|
|
37
35
|
{
|
|
@@ -42,26 +40,25 @@ npm i inversify zod reflect-metadata @alevnyacow/nzmt
|
|
|
42
40
|
}
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
3. Initialize
|
|
43
|
+
### 3. Initialize
|
|
46
44
|
|
|
47
45
|
```bash
|
|
48
46
|
npx nzmt init prismaClientPath:@/generated/prisma/client
|
|
49
47
|
```
|
|
50
48
|
|
|
51
|
-
This command generates:
|
|
49
|
+
This command takes absolute path to your Prisma client as input and generates:
|
|
52
50
|
|
|
53
51
|
- `nzmt.config.json` file
|
|
54
|
-
- DI
|
|
55
|
-
-
|
|
56
|
-
- Some infrastructure helpers also already injected in DI
|
|
52
|
+
- DI setup
|
|
53
|
+
- infrastructure helpers
|
|
57
54
|
|
|
58
|
-
4.
|
|
55
|
+
### 4. Plug Prisma adapter
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
Edit scaffolded `/server/infrastructure/prisma/client.ts` file
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
## π Making full-stack CRUD for `User` entity with React queries and Server Actions
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
Assuming you have `User` prisma schema.
|
|
65
62
|
|
|
66
63
|
```bash
|
|
67
64
|
npx nzmt crud-api user
|
|
@@ -78,39 +75,37 @@ This command generates:
|
|
|
78
75
|
|
|
79
76
|
Everything is wired automatically via DI β no manual setup needed.
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
3. Tweak `UserStore` schemas if needed in scaffolded `/server/stores/users/user.store.ts` file.
|
|
78
|
+
Then tweak a few files:
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
- `/shared/entities/user/user.entity.ts` β entity schema
|
|
81
|
+
- `/server/stores/users/user.store.ts` β store schemas (if default schemas do not fit your needs)
|
|
82
|
+
- `/server/stores/users/user.store.prisma.ts` β map `UserStore` contracts to Prisma client contracts
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
π One command + few tweaks β ready-to-use React Query hooks & Server Actions backend.
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
## βοΈ Using scaffolded React query hooks
|
|
90
87
|
|
|
91
88
|
```
|
|
92
89
|
Schema: Client β React Query β API β Controller β Service β Store β DB
|
|
93
90
|
```
|
|
94
91
|
|
|
95
|
-
Everything is already scaffolded and grouped in handy namespace for you, just import it and use! Even invalidations are working out of the box (though you can modify scaffolded queries any way you want)! β¨
|
|
96
|
-
|
|
97
92
|
```tsx
|
|
98
93
|
'use client'
|
|
99
94
|
|
|
100
95
|
import { UserQueries } from "@/client/shared/queries/user";
|
|
101
96
|
|
|
102
|
-
export default function
|
|
97
|
+
export default function () {
|
|
103
98
|
const { mutate: addUser } = UserQueries.usePOST()
|
|
104
99
|
const { data, isFetching } = UserQueries.useGET({ query: {} })
|
|
105
100
|
|
|
106
|
-
const
|
|
107
|
-
addUser({ body: { payload: { name:
|
|
101
|
+
const addRandomUser = () => {
|
|
102
|
+
addUser({ body: { payload: { name: `${Math.random()}` } } })
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
return (
|
|
111
106
|
<div>
|
|
112
|
-
<button onClick={
|
|
113
|
-
|
|
107
|
+
<button onClick={addRandomUser}>
|
|
108
|
+
New random user
|
|
114
109
|
</button>
|
|
115
110
|
|
|
116
111
|
{isFetching ? 'Loading users...' : JSON.stringify(data)}
|
|
@@ -120,14 +115,12 @@ export default function Home() {
|
|
|
120
115
|
|
|
121
116
|
```
|
|
122
117
|
|
|
123
|
-
|
|
118
|
+
## πΌ Using scaffolded Service methods as Next server actions
|
|
124
119
|
|
|
125
120
|
```
|
|
126
121
|
Schema: Server Action β Service β Store β DB
|
|
127
122
|
```
|
|
128
123
|
|
|
129
|
-
Just get required instances from DI and use methods. That's all. β¨
|
|
130
|
-
|
|
131
124
|
```tsx
|
|
132
125
|
'use server'
|
|
133
126
|
|
|
@@ -142,71 +135,55 @@ export default async function() {
|
|
|
142
135
|
*/
|
|
143
136
|
const userService = fromDI<UserService>('UserService')
|
|
144
137
|
|
|
145
|
-
const
|
|
146
|
-
filter: { id: '
|
|
138
|
+
const user1 = await userService.getDetails({
|
|
139
|
+
filter: { id: 'user-1-id' }
|
|
147
140
|
})
|
|
148
141
|
|
|
149
|
-
return <div>
|
|
150
|
-
Take a break, {JSON.stringify(driver8)}
|
|
151
|
-
{JSON.stringify(driver8)}, take a break
|
|
152
|
-
</div>
|
|
142
|
+
return <div>{JSON.stringify(user1)}</div>
|
|
153
143
|
}
|
|
154
144
|
```
|
|
155
145
|
|
|
156
|
-
#
|
|
146
|
+
# β FAQ
|
|
157
147
|
|
|
158
|
-
##
|
|
148
|
+
## What does DDD-inspired mean?
|
|
149
|
+
|
|
150
|
+
NZMT puts your business domain first. Entities drive the architecture, so backend and frontend stay consistent, and all layers are generated from your entity contracts and schemas.
|
|
151
|
+
|
|
152
|
+
## What does contract-first mean?
|
|
159
153
|
|
|
160
|
-
|
|
154
|
+
The behavior of all server modules in NZMT is governed by Zod schemas. Function signatures and entity contracts are derived from these schemas. There is also automatic runtime validation to ensure that all data β function arguments and entity models β conform to their schemas.
|
|
161
155
|
|
|
162
|
-
|
|
156
|
+
## Can I tweak scaffolded files?
|
|
163
157
|
|
|
164
|
-
|
|
158
|
+
Yes β everything is fully editable, including configuration. Think of NZMT as a shadcn-style approach for full-stack: scaffold first, then fully own the code. Moreover, your changes are preserved on subsequent generations. For example, if you modify a generated query and regenerate later, your edits stay intact.
|
|
165
159
|
|
|
166
160
|
## Do I really need to understand DI and other fancy concepts to use NZMT?
|
|
167
161
|
|
|
168
|
-
No. NZMT
|
|
162
|
+
No. NZMT handles dependency injection (DI) for you using `inversifyjs`. You donβt need to set it up manually.
|
|
163
|
+
To get an instance of a service anywhere in your server code, just use:
|
|
169
164
|
|
|
170
165
|
```tsx
|
|
166
|
+
import { fromDI } from '@/server/di'
|
|
167
|
+
|
|
171
168
|
const userService = fromDI<UserService>('UserService')
|
|
172
169
|
```
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
Good design is impossible without precise terminology. The definition of a "Repository" can vary depending on the terminology used. Itβs frustrating when youβve spent your whole life writing repositories, and then some smart aleck comes along and accuses you of having been writing, say, Data Access Objects all this time! In general, a "Repository" is simply a pattern for working with data. Often, what we really need isnβt a specific pattern, but a properly separated abstraction layer for data handling, which we can then adapt to our needs. Thatβs exactly why the names of the modules used for the Data Layer in NZMT are kept as abstract as possible, without tying them to any specific data-handling pattern.
|
|
177
|
-
|
|
178
|
-
This approach wasnβt invented here; it has already proven successful, for example, in Go.
|
|
179
|
-
|
|
180
|
-
## Why not just use plain Next.js?
|
|
171
|
+
Here, `fromDI` is strongly typed β your IDE will give autocomplete automatically.
|
|
181
172
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
NZMT removes the repetitive parts:
|
|
185
|
-
- validation
|
|
186
|
-
- API wiring
|
|
187
|
-
- client queries
|
|
188
|
-
- service layer
|
|
189
|
-
- data layer
|
|
190
|
-
|
|
191
|
-
So you can focus on your logic while NZMT handles boring tech stuff like folder structure and contracts.
|
|
173
|
+
## Why data layer modules are called `Stores` and not `Repositories`?
|
|
192
174
|
|
|
193
|
-
|
|
175
|
+
A βRepositoryβ is a specific design pattern for managing data. NZMT prefers Stores β a simple, flexible abstraction for your data layer that can adapt to your needs regardless of the specific pattern. This approach helps to keep your code simple, and it has been successfully used in other languages, like Go.
|
|
194
176
|
|
|
195
177
|
## Why not use Nest or tRPC?
|
|
196
178
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
`NZMT` sits between `tRPC` and `NestJS`:
|
|
200
|
-
|
|
201
|
-
- from tRPC β type safety and DX
|
|
202
|
-
- from NestJS β structure and layering, but more DDD-inspired
|
|
179
|
+
`NZMT` combines the best of both worlds while staying in plain Next.js:
|
|
203
180
|
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
- no magic runtime
|
|
207
|
-
- full control over your code
|
|
208
|
-
- power of react-query for client-server requests
|
|
209
|
-
- no new layers of client-server interaction
|
|
181
|
+
- From tRPC β type safety and developer experience
|
|
182
|
+
- From NestJS β structured architecture (but more DDD-inspired) with intuitive DI
|
|
210
183
|
|
|
211
|
-
|
|
184
|
+
Plus:
|
|
212
185
|
|
|
186
|
+
- No framework lock-in
|
|
187
|
+
- No magic runtime
|
|
188
|
+
- Full control over your code
|
|
189
|
+
- No extra client-server layers
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alevnyacow/nzmt",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.17",
|
|
4
4
|
"description": "Next Zod Modules Toolkit",
|
|
5
|
+
"keywords": ["next", "full-stack", "server", "backend", "cli", "scaffolder", "zod", "rest", "contract programming", "react-query", "ddd", "domain-driven"],
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "https://github.com/alevnyacow/nzmt.git"
|