@mee4dy/crud-nestjs 1.0.3

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 (53) hide show
  1. package/dist/constants/index.d.ts +1 -0
  2. package/dist/constants/index.js +5 -0
  3. package/dist/constants/index.js.map +1 -0
  4. package/dist/crud.controller.d.ts +22 -0
  5. package/dist/crud.controller.js +134 -0
  6. package/dist/crud.controller.js.map +1 -0
  7. package/dist/crud.service.d.ts +15 -0
  8. package/dist/crud.service.js +55 -0
  9. package/dist/crud.service.js.map +1 -0
  10. package/dist/decorators/crud-params.decorator.d.ts +5 -0
  11. package/dist/decorators/crud-params.decorator.js +53 -0
  12. package/dist/decorators/crud-params.decorator.js.map +1 -0
  13. package/dist/decorators/crud.decorator.d.ts +3 -0
  14. package/dist/decorators/crud.decorator.js +77 -0
  15. package/dist/decorators/crud.decorator.js.map +1 -0
  16. package/dist/decorators/index.d.ts +2 -0
  17. package/dist/decorators/index.js +19 -0
  18. package/dist/decorators/index.js.map +1 -0
  19. package/dist/exceptions/endpoint-disabled.exception.d.ts +4 -0
  20. package/dist/exceptions/endpoint-disabled.exception.js +14 -0
  21. package/dist/exceptions/endpoint-disabled.exception.js.map +1 -0
  22. package/dist/filters/crud-exception.filter.d.ts +4 -0
  23. package/dist/filters/crud-exception.filter.js +48 -0
  24. package/dist/filters/crud-exception.filter.js.map +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +19 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/services/crud-params-adapter.service.d.ts +14 -0
  29. package/dist/services/crud-params-adapter.service.js +147 -0
  30. package/dist/services/crud-params-adapter.service.js.map +1 -0
  31. package/dist/services/query-params-extractor.service.d.ts +14 -0
  32. package/dist/services/query-params-extractor.service.js +112 -0
  33. package/dist/services/query-params-extractor.service.js.map +1 -0
  34. package/dist/types/crud-config.types.d.ts +25 -0
  35. package/dist/types/crud-config.types.js +3 -0
  36. package/dist/types/crud-config.types.js.map +1 -0
  37. package/dist/types/crud-params.types.d.ts +40 -0
  38. package/dist/types/crud-params.types.js +3 -0
  39. package/dist/types/crud-params.types.js.map +1 -0
  40. package/dist/types/crud-query.types.d.ts +16 -0
  41. package/dist/types/crud-query.types.js +3 -0
  42. package/dist/types/crud-query.types.js.map +1 -0
  43. package/dist/types/crud-response.types.d.ts +9 -0
  44. package/dist/types/crud-response.types.js +3 -0
  45. package/dist/types/crud-response.types.js.map +1 -0
  46. package/dist/utils/merge.util.d.ts +1 -0
  47. package/dist/utils/merge.util.js +45 -0
  48. package/dist/utils/merge.util.js.map +1 -0
  49. package/dist/utils/replace-pk.util.d.ts +1 -0
  50. package/dist/utils/replace-pk.util.js +25 -0
  51. package/dist/utils/replace-pk.util.js.map +1 -0
  52. package/package.json +34 -0
  53. package/readme.md +426 -0
package/readme.md ADDED
@@ -0,0 +1,426 @@
1
+ # @mee4dy/crud-nestjs
2
+
3
+ Модуль для NestJS, предоставляющий декларативный и расширяемый CRUD-контроллер с минимальной конфигурацией. Позволяет быстро создавать REST API для любых моделей с поддержкой фильтрации, сортировки, пагинации, скопов и гибкой настройки.
4
+
5
+ ## Преимущества
6
+
7
+ - Декларативный подход через декоратор `@Crud`
8
+ - Не требует создания сервисов и наследования
9
+ - Гибкая настройка разрешённых параметров (allowedParams)
10
+ - Поддержка scopes (динамические параметры по каждому эндпоинту)
11
+ - Единый формат ответа и ошибок
12
+ - Лёгкая интеграция с клиентом (@mee4dy/crud-client)
13
+ - Расширяемость и чистый код
14
+
15
+ ## Установка
16
+
17
+ ```bash
18
+ npm install @mee4dy/crud-nestjs
19
+ ```
20
+
21
+ ## Быстрый старт
22
+
23
+ 1. Импортируйте пакет и используйте декоратор `@Crud` в контроллере:
24
+
25
+ ```typescript
26
+ import { Controller } from '@nestjs/common';
27
+ import { Crud } from '@mee4dy/crud-nestjs';
28
+ import { Post } from './posts.model';
29
+
30
+ @Crud({
31
+ repository: () => Posts,
32
+ })
33
+ @Controller('posts')
34
+ export class PostsController {}
35
+ ```
36
+
37
+ 2. Подключите модель к SequelizeModule в модуле:
38
+
39
+ ```typescript
40
+ import { Module } from '@nestjs/common';
41
+ import { SequelizeModule } from '@nestjs/sequelize';
42
+ import { PostsController } from './posts.controller';
43
+ import { Posts } from './posts.model';
44
+
45
+ @Module({
46
+ imports: [SequelizeModule.forFeature([Posts])],
47
+ controllers: [PostsController],
48
+ })
49
+ export class PostsModule {}
50
+ ```
51
+
52
+ ## Структура опций для декоратора @Crud
53
+
54
+ ```typescript
55
+ type CrudDecoratorConfig = {
56
+ /** Функция, возвращающая модель Sequelize для работы с БД */
57
+ repository: () => any;
58
+
59
+ /** Имя поля первичного ключа (по умолчанию 'id') */
60
+ pk?: string;
61
+
62
+ /** Включение/отключение CRUD-методов */
63
+ endpoints?: {
64
+ items?: boolean; // GET /items - получение списка
65
+ item?: boolean; // GET /items/:pk - получение одного элемента
66
+ create?: boolean; // POST /items - создание
67
+ update?: boolean; // PUT /items/:pk - обновление
68
+ delete?: boolean; // DELETE /items/:pk - удаление
69
+ };
70
+
71
+ /** Настройка разрешённых параметров из query string */
72
+ query?: {
73
+ allowedParams?: {
74
+ filters?: string[] | boolean; // Разрешённые поля для фильтрации
75
+ orders?: string[] | boolean; // Разрешённые поля для сортировки
76
+ groups?: string[] | boolean; // Разрешённые поля для группировки
77
+ limit?: boolean; // Разрешить пагинацию (limit)
78
+ offset?: boolean; // Разрешить пагинацию (offset)
79
+ fields?: string[]; // Разрешённые поля для выборки
80
+ fieldsExclude?: string[]; // Поля для исключения из выборки
81
+ };
82
+ };
83
+
84
+ /** Параметры по умолчанию для всех запросов */
85
+ params?: {
86
+ default?: Partial<CrudParams>;
87
+ };
88
+
89
+ /** Динамические параметры для каждого эндпоинта */
90
+ scopes?: {
91
+ global?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Применяется ко всем эндпоинтам
92
+ items?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Только для получения списка
93
+ item?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Только для получения одного элемента
94
+ create?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Только для создания
95
+ update?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Только для обновления
96
+ delete?: (req: any) => Partial<{ params: Partial<CrudParams> }>; // Только для удаления
97
+ };
98
+ };
99
+ ```
100
+
101
+ ## Тип CrudParams
102
+
103
+ `CrudParams` — это основной тип для описания параметров запроса к базе данных.
104
+
105
+ ```typescript
106
+ interface CrudParams {
107
+ filters?: CrudParamsFilter; // Фильтры для WHERE условий
108
+ join?: CrudJoin[]; // Join связи
109
+ fields?: CrudFields; // Настройка выборки полей
110
+ orders?: CrudOrder[]; // Сортировка (ORDER BY)
111
+ groups?: string[]; // Группировка (GROUP BY)
112
+ limit?: number; // Лимит записей
113
+ offset?: number; // Смещение для пагинации
114
+ }
115
+ ```
116
+
117
+ ### Фильтры (filters)
118
+
119
+ ```typescript
120
+ type CrudParamsFilter = {
121
+ [field: string]: CrudFieldFilter;
122
+ };
123
+
124
+ type CrudFieldFilter =
125
+ | string // Простое равенство: { user_id: "123" }
126
+ | number // Простое равенство: { status: 1 }
127
+ | boolean // Простое равенство: { active: true }
128
+ | { op: 'eq'; value: string | number | boolean } // Явное равенство
129
+ | { op: 'like'; value: string } // LIKE поиск
130
+ | { op: 'range' | 'period'; from: number | string; to: number | string }; // Диапазон значений
131
+ ```
132
+
133
+ **Примеры фильтров:**
134
+
135
+ ```typescript
136
+ // В коде:
137
+ const filters = {
138
+ user_id: 1, // Простое равенство
139
+ title: { op: 'like', value: '%test%' }, // LIKE поиск
140
+ created_at: { op: 'range', from: '2024-01-01', to: '2024-12-31' }, // Диапазон дат
141
+ status: { op: 'eq', value: 'active' } // Явное равенство
142
+ };
143
+
144
+ // HTTP запрос:
145
+ GET /posts?filters[user_id]=1&filters[title][op]=like&filters[title][value]=test&filters[created_at][op]=range&filters[created_at][from]=2024-01-01&filters[created_at][to]=2024-12-31
146
+ ```
147
+
148
+ ### Сортировка (orders)
149
+
150
+ ```typescript
151
+ type CrudOrder = [string, 'asc' | 'desc']; // [поле, направление]
152
+
153
+ // В коде:
154
+ const orders = [
155
+ ['created_at', 'desc'], // Сначала новые
156
+ ['title', 'asc'], // По алфавиту
157
+ ['id', 'desc'] // По ID убывание
158
+ ];
159
+
160
+ // HTTP запрос:
161
+ GET /posts?orders[0][0]=created_at&orders[0][1]=desc&orders[1][0]=title&orders[1][1]=asc
162
+ ```
163
+
164
+ ### Группировка (groups)
165
+
166
+ ```typescript
167
+ // В коде:
168
+ const groups = ['user_id', 'status'];
169
+
170
+ // HTTP запрос:
171
+ GET /posts?groups[0]=user_id&groups[1]=status
172
+ ```
173
+
174
+ ### Пагинация (limit, offset)
175
+
176
+ ```typescript
177
+ // В коде:
178
+ const limit = 20; // Количество записей на странице
179
+ const offset = 40; // Пропустить первые 40 записей (для 3-й страницы)
180
+
181
+ // HTTP запрос:
182
+ GET /posts?limit=20&offset=40
183
+ ```
184
+
185
+ ### Выборка полей (fields)
186
+
187
+ ```typescript
188
+ type CrudFields = {
189
+ include?: [Literal, CrudField][]; // Дополнительные SQL выражения
190
+ exclude?: CrudField[]; // Исключаемые поля
191
+ };
192
+
193
+ // В коде:
194
+ const fields = {
195
+ include: [
196
+ [Sequelize.literal('CONCAT(name, " (", email, ")")'), 'full_info'],
197
+ [Sequelize.literal('COUNT(*)'), 'total_count'],
198
+ ],
199
+ exclude: ['password', 'deleted_at'],
200
+ };
201
+ ```
202
+
203
+ ### Связи (join)
204
+
205
+ ```typescript
206
+ type CrudJoin = {
207
+ repository: () => any; // Функция, возвращающая модель Sequelize
208
+ attributes?: CrudFields; // Настройка полей для связанной модели
209
+ join?: CrudJoin[]; // Вложенные join для связанной модели
210
+ };
211
+ ```
212
+
213
+ **Примеры join связей:**
214
+
215
+ ```typescript
216
+ // Простой join с пользователем
217
+ const join = [
218
+ {
219
+ repository: () => User,
220
+ attributes: {
221
+ include: [[Sequelize.literal('CONCAT(name, " (", email, ")")'), 'full_info']],
222
+ exclude: ['password', 'deleted_at'],
223
+ },
224
+ },
225
+ ];
226
+
227
+ // Вложенный join: посты с комментариями и пользователями комментариев
228
+ const join = [
229
+ {
230
+ repository: () => Comment,
231
+ attributes: {
232
+ include: [[Sequelize.literal('CONCAT("[", id, "] ", text)'), 'formatted_text']],
233
+ },
234
+ join: [
235
+ {
236
+ repository: () => User,
237
+ attributes: {
238
+ exclude: ['password', 'deleted_at'],
239
+ },
240
+ },
241
+ ],
242
+ },
243
+ ];
244
+
245
+ // Множественные join
246
+ const join = [
247
+ {
248
+ repository: () => User,
249
+ attributes: {
250
+ exclude: ['password'],
251
+ },
252
+ },
253
+ {
254
+ repository: () => Category,
255
+ attributes: {
256
+ include: [[Sequelize.literal('UPPER(name)'), 'upper_name']],
257
+ },
258
+ },
259
+ ];
260
+ ```
261
+
262
+ ## Примеры контроллеров
263
+
264
+ ### Минимальный контроллер
265
+
266
+ ```typescript
267
+ @Crud({ repository: () => Users })
268
+ @Controller('users')
269
+ export class UsersController {}
270
+ ```
271
+
272
+ ### Контроллер с ограничением параметров
273
+
274
+ ```typescript
275
+ @Crud({
276
+ repository: () => Posts,
277
+ query: {
278
+ allowedParams: {
279
+ filters: ['id', 'user_id'],
280
+ orders: ['id', 'created_at'],
281
+ limit: true,
282
+ offset: true,
283
+ },
284
+ },
285
+ })
286
+ @Controller('posts')
287
+ export class PostsController {}
288
+ ```
289
+
290
+ ### Контроллер с default params и scopes по эндпоинтам
291
+
292
+ ```typescript
293
+ @Crud({
294
+ repository: () => Comments,
295
+ params: {
296
+ default: {
297
+ orders: [['id', 'desc']],
298
+ limit: 10,
299
+ },
300
+ },
301
+ scopes: {
302
+ global: (req) => ({
303
+ params: {
304
+ filters: {
305
+ deleted_at: null,
306
+ },
307
+ },
308
+ }),
309
+ items: (req) => ({
310
+ params: {
311
+ filters: {
312
+ user_id: req.user.id,
313
+ },
314
+ },
315
+ }),
316
+ create: (req) => ({
317
+ params: {
318
+ filters: {
319
+ user_id: req.user.id,
320
+ },
321
+ },
322
+ }),
323
+ },
324
+ })
325
+ @Controller('comments')
326
+ export class CommentsController {}
327
+ ```
328
+
329
+ ### Контроллер с join связями
330
+
331
+ ```typescript
332
+ @Crud({
333
+ repository: () => Posts,
334
+ params: {
335
+ default: {
336
+ join: [
337
+ {
338
+ repository: () => Users,
339
+ attributes: {
340
+ exclude: [
341
+ 'password',
342
+ 'deleted_at'
343
+ ]
344
+ }
345
+ }
346
+ ]
347
+ }
348
+ },
349
+ scopes: {
350
+ item: (req) => ({
351
+ params: {
352
+ join: [
353
+ {
354
+ repository: () => Comments,
355
+ attributes: {
356
+ include: [
357
+ [
358
+ Sequelize.literal('CONCAT("[", id, "] ", text)'),
359
+ 'formatted_text'
360
+ ]
361
+ ]
362
+ },
363
+ join: [
364
+ {
365
+ repository: () => Users,
366
+ attributes: {
367
+ exclude: [
368
+ 'password'
369
+ ]
370
+ }
371
+ }
372
+ ]
373
+ }
374
+ ]
375
+ }
376
+ })
377
+ }
378
+ })
379
+ @Controller('posts')
380
+ ```
381
+
382
+ ## Формат ответа
383
+
384
+ Все ответы возвращаются в едином формате:
385
+
386
+ ```json
387
+ {
388
+ "status": true,
389
+ "data": {
390
+ "items": [ ... ],
391
+ "item": { ... }
392
+ }
393
+ }
394
+ ```
395
+
396
+ В случае ошибки:
397
+
398
+ ```json
399
+ {
400
+ "status": false,
401
+ "error": {
402
+ "message": "...",
403
+ "statusCode": 400,
404
+ "errorType": "..."
405
+ }
406
+ }
407
+ ```
408
+
409
+ ## Обработка ошибок
410
+
411
+ - Все ошибки автоматически форматируются через фильтр `CrudExceptionFilter`
412
+ - Для отключённых эндпоинтов выбрасывается `EndpointDisabledException`
413
+
414
+ ## Интеграция с клиентом
415
+
416
+ Пакет полностью совместим с [@mee4dy/crud-client](../@mee4dy/crud-client) для фронтенда.
417
+
418
+ ## Совместимость
419
+
420
+ - NestJS >= 9
421
+ - Sequelize >= 6
422
+ - Node.js >= 16
423
+
424
+ ## Лицензия
425
+
426
+ MIT