@alevnyacow/nzmt 0.15.16 → 0.15.18

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.
Files changed (2) hide show
  1. package/README.md +131 -168
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -6,13 +6,31 @@
6
6
 
7
7
  # What
8
8
 
9
- Next Zod Modules Toolkit. Next.js tools you actually missed + a scaffolder for server logic & client queries. Not a framework. Full-stack, batteries included.
9
+ Next Zod Modules Toolkit. Next.js tools you actually missed + a scaffolder for server logic & client queries. **Not a framework.** Full-stack, batteries included.
10
+
11
+ Build full-stack features in Next.js without boilerplate. ⚡
12
+
13
+ # TL;DR
14
+
15
+ One command:
16
+
17
+ `npx nzmt crud-api user`
18
+
19
+ Gives you:
20
+
21
+ - entity and stores (Prisma and in-memory)
22
+ - fully typed API routes
23
+ - services (for Server Actions)
24
+ - Zod validation
25
+ - React Query hooks
26
+
27
+ All wired together and fully editable. No boilerplate. See `Quick start with Prisma` for a full working example.
10
28
 
11
29
  # Why
12
30
 
13
31
  - ☕ Keep using plain Next.js — just faster and cleaner. Skip the moment when some “helpful” framework fights you, making you wonder if coding it yourself would’ve been easier.
14
32
  - 🧙 Focus on your domain logic without drowning in full-blown DDD.
15
- - ✨ DI, Zod validation, project structure & API controllers out of the box.
33
+ - ✨ DI, Zod validation, project structure, handy API controllers and Server actions out of the box.
16
34
  - 🪄 Services, controllers, client queries, and other programmer stuff appear at the snap of a finger — and yes, it’s fun. (Well, not *literally* at the snap of a finger — that’s just marketing, to be honest. You still need to run one CLI command.)
17
35
 
18
36
  # Quick start with Prisma
@@ -21,228 +39,173 @@ Assuming you have a Next.js project with a generated Prisma client, and configur
21
39
 
22
40
  ## Setup phase
23
41
 
24
- ```bash
25
- # 1. Install NZMT and dependencies
26
- npm i inversify zod reflect-metadata @alevnyacow/nzmt
27
-
28
- # 2. Enable decorators in tsconfig.json
29
- # {
30
- # "compilerOptions": {
31
- # "experimentalDecorators": true,
32
- # "emitDecoratorMetadata": true
33
- # }
34
- # }
35
-
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.
42
+ 1. Install NZMT and dependencies:
45
43
 
46
44
  ```bash
47
- npx nzmt crud-api user
45
+ npm i inversify zod reflect-metadata @alevnyacow/nzmt
48
46
  ```
49
47
 
50
- Let's break down what's been scaffolded after this command:
51
-
52
- ### 1. Scaffolded `UserEntity`
48
+ 2. Enable decorators in tsconfig.json
53
49
 
54
50
  ```ts
55
- /** /shared/entities/user/user.entity.ts **/
56
-
57
- import z from 'zod'
58
- import { ValueObjects } from '@alevnyacow/nzmt'
59
-
60
- export type UserModel = z.infer<typeof User.schema>
61
-
62
- export class User {
63
- static schema = z.object({
64
- id: ValueObjects.Identifier.schema,
65
-
66
- })
67
-
68
- private constructor(private readonly data: UserModel) {}
69
-
70
- static create = (data: UserModel) => {
71
- const parsedModel = User.schema.parse(data)
72
- return new User(parsedModel)
73
- }
74
-
75
- get model(): UserModel {
76
- return this.data
77
- }
51
+ {
52
+ "compilerOptions": {
53
+ "experimentalDecorators": true,
54
+ "emitDecoratorMetadata": true
55
+ }
78
56
  }
79
57
  ```
80
58
 
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..
59
+ 3. Initialize NZMT with the absolute Prisma client path as a parameter
82
60
 
83
- ### 2. Scaffolded `UserStore` contract with `RAM` (in-memory) and `Prisma` implementations
61
+ ```bash
62
+ npx nzmt init prismaClientPath:@/generated/prisma/client
63
+ ```
84
64
 
85
- We'll cover two of generated files that need your attention. First is a general user store description.
65
+ This command generates:
86
66
 
87
- ```ts
88
- /** /server/stores/users/user.store.ts **/
67
+ - `nzmt.config.json` file
68
+ - DI infrastructure boilerplate
69
+ - Prisma Client instance injected in DI
70
+ - Some infrastructure helpers also already injected in DI
89
71
 
90
- import { Store } from '@alevnyacow/nzmt'
91
- import { User } from '@/shared/entities/user'
72
+ 4. Import and set up there the necessary Prisma adapter in `/server/infrastructure/prisma/client.ts`
92
73
 
93
- export const userStoreMetadata = {
94
- models: {
95
- list: User.schema,
96
- details: User.schema,
97
- },
74
+ ## Example 1. CRUD for `User` entity with API route handlers and Server Actions
98
75
 
99
- searchPayload: {
100
- list: User.schema.omit({ id: true }),
101
- specific: User.schema.pick({ id: true }),
102
- },
103
-
104
- actionsPayload: {
105
- create: User.schema.omit({ id: true }),
106
- update: User.schema.omit({ id: true }).partial(),
107
- },
76
+ Assuming you have `User` prisma schema.
108
77
 
109
- name: 'UserStore'
110
- } satisfies Store.Metadata
78
+ 1. Run NZMT scaffolder:
111
79
 
112
- ...scaffolded code
80
+ ```bash
81
+ npx nzmt crud-api user
113
82
  ```
114
83
 
115
- Contracts are pretty self-explainatory, but let's break this down anyway.
84
+ This command generates:
116
85
 
117
- Models:
86
+ - `UserEntity`
87
+ - `UserStore` (with Prisma + RAM implementations)
88
+ - `UserService` (ready to be used in Server Actions)
89
+ - `UserController` proxying UserService methods
90
+ - User `API routes` for UserController endpoints
91
+ - `React Query hooks` for fetching UserController from client-side
118
92
 
119
- - `models.list` - list model, `list` method will return list of those entities
120
- - `models.details` - details model, `details` will return this entity
93
+ Everything is wired automatically via DI no manual setup needed.
121
94
 
122
- Search payload:
95
+ 2. Describe your entity in `/shared/entities/user/user.entity.ts` (look at static `schema` field)
123
96
 
124
- - `searchPayload.list` - how to filter data in `list` method
125
- - `searchPayload.specific` - how to find one specific entity in `details` method
97
+ 3. Tweak `UserStore` schemas if needed in `/server/stores/users/user.store.ts`
126
98
 
127
- Actions payload:
99
+ 4. Describe how your `UserStore` contracts map to your `Prisma` client contracts in `server/stores/users/user.store.prisma.ts` (look at `mappers` object)
128
100
 
129
- - `actionsPayload.create` - what is needed to create a new entity
130
- - `actionsPayload.update` - what is needed to update an entity. Note: only update payload must be here, filters will be used from `searchPayload`
101
+ And after one CLI command and few tweaks you can use your React query hooks or Server actions. 🪄
131
102
 
132
- All of these schemas can be modified. They don’t even have to be derived from the entity’s schema, but doing so is strongly recommended for consistency.
103
+ ### How to use React query hooks
133
104
 
134
- Second file is a `Prisma` implementation:
105
+ ```
106
+ Schema: Client → React Query → API → Controller → Service → Store → DB
107
+ ```
135
108
 
136
- ```ts
137
- /** /server/stores/users/user.store.prisma */
138
-
139
- ...scaffolded code
140
-
141
- const mappers = {
142
- toFindOnePayload: (source: Types['findOnePayload']): Prisma.UserWhereUniqueInput => {
143
- return {
144
-
145
- };
146
- },
147
- toFindListPayload: (source: Types['findListPayload']): Prisma.UserWhereInput => {
148
- return {
149
-
150
- };
151
- },
152
- ... few other mappers
109
+ Everything is already scaffolded for you, just import it and use! ✨
110
+
111
+ ```tsx
112
+ 'use client'
113
+
114
+ import { useUserAPI_GET } from "@/client/shared/queries/user-controller/GET";
115
+ import { useUserAPI_POST } from "@/client/shared/queries/user-controller/POST";
116
+
117
+ export default function Home() {
118
+ const { mutate: addUser } = useUserAPI_POST()
119
+ const { data, isFetching } = useUserAPI_GET({ query: {} })
120
+
121
+ const addGreg = () => {
122
+ addUser({ body: { payload: { name: 'Greg' } } })
123
+ }
124
+
125
+ return (
126
+ <div>
127
+ <button onClick={addGreg}>
128
+ Add Greg
129
+ </button>
130
+
131
+ {isFetching ? 'Loading users...' : JSON.stringify(data)}
132
+ </div>
133
+ );
153
134
  }
154
135
 
155
- @injectable()
156
- export class UserPrismaStore implements UserStore {
157
- ..scaffolded code
158
136
  ```
159
137
 
160
- Pretty cool, right? All you need to do is implementing `mappers` and in the vast majority of cases it's enough to have working `Prisma` store. And even more - `RAM` implementation works out of the box! ✨
161
-
162
- ### 3. Scaffolded `UserService` with all business methods
138
+ ### How to use server actions
163
139
 
164
- Works out of the box. ✨
165
-
166
- ### 4. Scaffolded `UserController` with ready-to-use API endpoints
140
+ ```
141
+ Schema: Server Action → Service → Store → DB
142
+ ```
167
143
 
168
- Works out of the box. ✨
144
+ Just get required instances from DI and use methods. That's all.
169
145
 
170
- ### 5. Scaffolded `Route handlers` in `/app/api`
146
+ ```tsx
147
+ 'use server'
171
148
 
172
- Works out of the box.
149
+ import { fromDI } from "@/server/di"
150
+ import type { UserService } from "@/server/services/user"
173
151
 
174
- ### 6. Scaffolded `React queries` for all controller methods
152
+ export default async function() {
153
+ const userService = fromDI<UserService>('UserService')
175
154
 
176
- Also works out of the box! ✨
155
+ const driver8 = await userService.getDetails({
156
+ filter: { id: 'driver-8' }
157
+ })
177
158
 
178
- Let's take a look at `client/shared/queries/user-controller/GET.ts` for example:
159
+ return <div>
160
+ Take a break, {JSON.stringify(driver8)}
161
+ {JSON.stringify(driver8)}, take a break
162
+ </div>
163
+ }
164
+ ```
179
165
 
180
- ```ts
181
- import { useQuery } from '@tanstack/react-query'
182
- import type { UserAPI } from '@/server/controllers/user'
183
- import { apiRequest } from '@/client/shared/utils'
166
+ # Common questions
184
167
 
185
- type Method = UserAPI['endpoints']['GET']
168
+ ## Do I really need to understand DI and other fancy concepts to use NZMT?
186
169
 
187
- const endpoint = '/api/user-controller'
170
+ No. NZMT provides you safe and intuitive facade above `inversifyjs` and automatically registers dependencies. To get an instance you just use `fromDI` function with strongly typed keys like this:
188
171
 
189
- export const useUserAPI_GET = (payload: Method['payload']) => {
190
- return useQuery<Method['response'], Method['error']>({
191
- queryKey: [endpoint, payload],
192
- queryFn: () => apiRequest(endpoint, 'GET')(payload)
193
- })
194
- }
172
+ ```tsx
173
+ const userService = fromDI<UserService>('UserService')
195
174
  ```
196
175
 
197
- - Fully typed and ready for client-side use.
198
- - `apiRequest` handles endpoint, method, and payload conveniently (also scaffolded and editable).
176
+ ## Can I tweak scaffolded files?
199
177
 
200
- And once again - **all code is editable - you stay in full control! 🔨⚙️**
178
+ Yes everything is fully editable, including configuration. You can think of NZMT as shadcn-style approach for server-side logic scaffold, then fully own the code.
201
179
 
202
- Now you can start building your domain logic — NZMT handles the boilerplate for you. 🪄
180
+ ## Why not just use plain Next.js?
203
181
 
204
- ## Example 2. CRUD for `Product` entity with only server actions
182
+ You can.
205
183
 
206
- Assuming you have `Product` prisma schema.
184
+ NZMT removes the repetitive parts:
185
+ - validation
186
+ - API wiring
187
+ - client queries
188
+ - service layer
189
+ - data layer
207
190
 
208
- ```bash
209
- npx nzmt crud-service product
210
- ```
191
+ So you can focus on your logic while NZMT handles boring tech stuff like folder structure and contracts.
211
192
 
212
- Command `crud-service` is a lot like the `crud-api` command, but it stops after generating service. So, you need to:
193
+ P.S. In general, you remain within plain Next.js.
213
194
 
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.
195
+ ## Why not use Nest or tRPC?
217
196
 
218
- `Services` can be used in Server Actions. Well, basically `Stores` can be used too as they are injected the same way as services, but it's strongly recommended to use services. Also, when you make `crud-api`, this generated service can also be used in Server Actions.
197
+ Again, you can use whatever you want, God bless you.
219
198
 
220
- Using services in Server Actions is very simple. You only need an instance of the service and then you can call required methods. To get an instance, use `fromDI` function, which was scaffolded when you initialized NMZT. Let's take a look at combined example with two services we've just created:
199
+ `NZMT` sits between `tRPC` and `NestJS`:
221
200
 
222
- ```tsx
223
- 'use server'
201
+ - from tRPC — type safety and DX
202
+ - from NestJS — structure and layering
224
203
 
225
- import { fromDI } from "@/server/di"
226
- import type { UserService } from "@/server/services/user"
227
- import type { ProductService } from "@/server/services/product"
204
+ But:
205
+ - no framework lock-in
206
+ - no magic runtime
207
+ - full control over your code
228
208
 
229
- export default async function() {
230
- // keys in fromDI function are strongly typed
231
- const userService = fromDI<UserService>('UserService')
232
- const productService = fromDI<ProductService>('ProductService')
209
+ Just better Next.js.
233
210
 
234
- const driver8 = await userService.getDetails({
235
- filter: { id: 'driver-8' }
236
- })
237
- const allProducts = await productService.getList({
238
- filter: { }
239
- })
240
211
 
241
- return <div>
242
- Take a break, {JSON.stringify(driver8)}
243
- {JSON.stringify(driver8)}, take a break
244
-
245
- Also we've got some products: {JSON.stringify(allProducts)}
246
- </div>
247
- }
248
- ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alevnyacow/nzmt",
3
- "version": "0.15.16",
3
+ "version": "0.15.18",
4
4
  "description": "Next Zod Modules Toolkit",
5
5
  "repository": {
6
6
  "type": "git",