@kollors/deep-json-server 1.0.0-alpha.8 → 1.0.0-beta.1
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 +279 -217
- package/README.ru.md +282 -220
- package/dist/index.d.ts +1 -2
- package/dist/src/auth/contract.d.ts +3 -2
- package/dist/src/auth/contract.js.map +1 -1
- package/dist/src/auth/service.js +1 -1
- package/dist/src/auth/service.js.map +1 -1
- package/dist/src/auth/store.d.ts +1 -1
- package/dist/src/auth/store.js.map +1 -1
- package/dist/src/cli/index.d.ts +2 -2
- package/dist/src/cli/index.js +59 -80
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/core/constants.d.ts +1 -1
- package/dist/src/core/constants.js +1 -1
- package/dist/src/core/constants.js.map +1 -1
- package/dist/src/core/database.d.ts +6 -11
- package/dist/src/core/database.js +2 -2
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/engine.d.ts +3 -15
- package/dist/src/core/engine.js +10 -69
- package/dist/src/core/engine.js.map +1 -1
- package/dist/src/core/lifecycle/mutation.js +4 -4
- package/dist/src/core/lifecycle/mutation.js.map +1 -1
- package/dist/src/core/lifecycle/options.d.ts +15 -0
- package/dist/src/core/lifecycle/options.js +13 -0
- package/dist/src/core/lifecycle/options.js.map +1 -1
- package/dist/src/core/model.d.ts +17 -2
- package/dist/src/core/model.js +28 -7
- package/dist/src/core/model.js.map +1 -1
- package/dist/src/core/mutations/write.js +4 -0
- package/dist/src/core/mutations/write.js.map +1 -1
- package/dist/src/core/paths.d.ts +1 -1
- package/dist/src/core/paths.js +5 -5
- package/dist/src/core/paths.js.map +1 -1
- package/dist/src/core/query/execute.d.ts +25 -0
- package/dist/src/core/query/execute.js +82 -0
- package/dist/src/core/query/execute.js.map +1 -0
- package/dist/src/core/query/filter.js +1 -1
- package/dist/src/core/query/filter.js.map +1 -1
- package/dist/src/core/query/options.js +2 -2
- package/dist/src/core/query/options.js.map +1 -1
- package/dist/src/core/records.d.ts +2 -1
- package/dist/src/core/records.js +5 -2
- package/dist/src/core/records.js.map +1 -1
- package/dist/src/core/storage.d.ts +2 -0
- package/dist/src/core/storage.js +2 -0
- package/dist/src/core/storage.js.map +1 -0
- package/dist/src/files/contract.d.ts +6 -7
- package/dist/src/files/contract.js.map +1 -1
- package/dist/src/files/disk-metadata.d.ts +9 -0
- package/dist/src/files/disk-metadata.js +44 -0
- package/dist/src/files/disk-metadata.js.map +1 -0
- package/dist/src/files/disk-paths.d.ts +15 -0
- package/dist/src/files/disk-paths.js +100 -0
- package/dist/src/files/disk-paths.js.map +1 -0
- package/dist/src/files/disk-store.js +21 -165
- package/dist/src/files/disk-store.js.map +1 -1
- package/dist/src/files/index.d.ts +1 -1
- package/dist/src/files/index.js +5 -2
- package/dist/src/files/index.js.map +1 -1
- package/dist/src/files/memory-store.js +3 -3
- package/dist/src/files/memory-store.js.map +1 -1
- package/dist/src/graphql/generate.d.ts +3 -2
- package/dist/src/graphql/generate.js +3 -1
- package/dist/src/graphql/generate.js.map +1 -1
- package/dist/src/graphql/preflight.js +2 -0
- package/dist/src/graphql/preflight.js.map +1 -1
- package/dist/src/graphql/resolvers.js +7 -3
- package/dist/src/graphql/resolvers.js.map +1 -1
- package/dist/src/graphql/routes.js +4 -1
- package/dist/src/graphql/routes.js.map +1 -1
- package/dist/src/graphql/schema.js +5 -2
- package/dist/src/graphql/schema.js.map +1 -1
- package/dist/src/openapi/document.js +30 -19
- package/dist/src/openapi/document.js.map +1 -1
- package/dist/src/openapi/generate.js +3 -1
- package/dist/src/openapi/generate.js.map +1 -1
- package/dist/src/openapi/options.d.ts +0 -2
- package/dist/src/openapi/registry.d.ts +11 -0
- package/dist/src/openapi/registry.js +20 -0
- package/dist/src/openapi/registry.js.map +1 -0
- package/dist/src/rest/options.d.ts +11 -3
- package/dist/src/rest/options.js +15 -0
- package/dist/src/rest/options.js.map +1 -1
- package/dist/src/rest/projection.d.ts +15 -3
- package/dist/src/rest/projection.js +49 -15
- package/dist/src/rest/projection.js.map +1 -1
- package/dist/src/rest/routes.js +18 -7
- package/dist/src/rest/routes.js.map +1 -1
- package/dist/src/server/bootstrap.d.ts +7 -0
- package/dist/src/server/bootstrap.js +48 -0
- package/dist/src/server/bootstrap.js.map +1 -0
- package/dist/src/server/config.d.ts +28 -30
- package/dist/src/server/config.js +82 -169
- package/dist/src/server/config.js.map +1 -1
- package/dist/src/server/create.d.ts +3 -5
- package/dist/src/server/create.js +24 -89
- package/dist/src/server/create.js.map +1 -1
- package/dist/src/server/features.d.ts +0 -11
- package/dist/src/server/features.js +0 -20
- package/dist/src/server/features.js.map +1 -1
- package/dist/src/server/http.d.ts +12 -0
- package/dist/src/server/http.js +40 -0
- package/dist/src/server/http.js.map +1 -0
- package/dist/src/server/model.d.ts +6 -0
- package/dist/src/server/model.js +14 -0
- package/dist/src/server/model.js.map +1 -0
- package/dist/src/server/openapi-options.d.ts +6 -0
- package/dist/src/server/openapi-options.js +15 -0
- package/dist/src/server/openapi-options.js.map +1 -0
- package/dist/src/server/public.d.ts +1 -2
- package/package.json +3 -3
package/README.ru.md
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
JSON-сервер для имитации API: REST, GraphQL, связанные записи, загрузка файлов и экспорт схем. Поддерживает вход пользователей, права владельца и администратора, даты записей и мягкое удаление. Требуется Node.js 22 или новее.
|
|
6
6
|
|
|
7
|
-
**1.0.0-
|
|
7
|
+
**Breaking changes: 1.0.0-beta.1.** `*` в REST `scope` теперь выбирает только скалярные поля. Массивы, объекты и связи указываются явно. При включённом auth права на запись доступны через виртуальное поле `actions`.
|
|
8
8
|
|
|
9
9
|
## Установка
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npm install @kollors/deep-json-server@
|
|
12
|
+
npm install @kollors/deep-json-server@beta
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Для установки конкретной версии укажите `@1.0.0-
|
|
15
|
+
Для установки конкретной версии укажите `@1.0.0-beta.1`.
|
|
16
16
|
|
|
17
17
|
## Быстрый старт
|
|
18
18
|
|
|
@@ -31,9 +31,7 @@ npm install @kollors/deep-json-server@alpha
|
|
|
31
31
|
`server.config.js`:
|
|
32
32
|
|
|
33
33
|
```js
|
|
34
|
-
export default {
|
|
35
|
-
database: { path: './database.json' },
|
|
36
|
-
};
|
|
34
|
+
export default { storage: 'file', database: { source: './database.json' } };
|
|
37
35
|
```
|
|
38
36
|
|
|
39
37
|
```sh
|
|
@@ -46,47 +44,53 @@ npx deep-json-server server.config.js
|
|
|
46
44
|
|
|
47
45
|
## Конфигурация
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
При `storage: 'file'` все источники и схема задаются путями, при `'memory'` — данными в памяти. Режимы нельзя смешивать. Наличие секций `auth`, `files`, `graphql` и `openapi` включает соответствующие модули. `graphql: {}` и `openapi: {}` включают только HTTP-маршруты со стандартными адресами; для экспорта добавьте `target`.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
export default {
|
|
51
|
+
storage: 'file',
|
|
52
|
+
database: { source: './database.json', schema: './schema.json' },
|
|
53
|
+
auth: { source: './users.json', expiresIn: 3600 },
|
|
54
|
+
files: { source: './uploads' },
|
|
55
|
+
graphql: { target: './generated/schema.graphql' },
|
|
56
|
+
openapi: { target: './generated/openapi.yaml' },
|
|
57
|
+
server: { host: '127.0.0.1', port: 4001 },
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Настройка | Назначение |
|
|
50
62
|
|---|---|
|
|
51
|
-
| `
|
|
52
|
-
| `database.
|
|
53
|
-
| `database.
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `graphql.
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `auth.expiresIn` | Срок действия сессии в секундах; по умолчанию 3600 |
|
|
63
|
+
| `storage` | Обязательный режим: `file` или `memory`; общий для всех источников и схемы |
|
|
64
|
+
| `database.source` | Путь к JSON базы или объект коллекций |
|
|
65
|
+
| `database.schema` | Путь к JSON схемы или объект схемы; необязателен для REST |
|
|
66
|
+
| `auth.source` | Путь к JSON пользователей или массив пользователей |
|
|
67
|
+
| `auth.expiresIn` | Срок сессии в секундах; по умолчанию 3600 |
|
|
68
|
+
| `files.source` | Каталог файлов или массив начальных файлов |
|
|
69
|
+
| `files.metadata` | Только для `file`: путь JSON метаданных; по умолчанию `.files.json` внутри `files.source` |
|
|
70
|
+
| `graphql.endpoint` | HTTP-маршрут; по умолчанию `/graphql` |
|
|
71
|
+
| `graphql.target` | Файл для экспорта GraphQL SDL |
|
|
72
|
+
| `openapi.endpoint` | HTTP-маршрут; по умолчанию `/openapi.json` |
|
|
73
|
+
| `openapi.target` | Файл для экспорта OpenAPI |
|
|
74
|
+
| `openapi.info` | Метаданные: обязательные `title`, `version`, необязательный `description` |
|
|
64
75
|
| `server.host`, `server.port` | По умолчанию `127.0.0.1`, `4001`; CLI также читает `HOST`/`PORT` |
|
|
65
76
|
| `server.pageSize`, `server.maxPageSize` | По умолчанию 10 и 100; размер по умолчанию ограничен максимумом |
|
|
66
|
-
| `server.cors`, `server.logger` | По умолчанию `true`; logger также
|
|
77
|
+
| `server.cors`, `server.logger` | По умолчанию `true`; logger принимает также настройки Fastify |
|
|
67
78
|
| `server.maxFileSize` | По умолчанию 100 МиБ |
|
|
68
|
-
| `files.data` | Бинарные файлы в памяти |
|
|
69
|
-
| `files.directory`, `files.metadata` | Каталог файлов и JSON метаданных; необходимы оба |
|
|
70
|
-
|
|
71
|
-
Относительные пути отсчитываются от каталога конфигурационного файла. При вызове `createServer()` с объектом конфигурации — от рабочего каталога. Сервер работает с копией переданных данных в памяти и метаданных.
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
Относительные пути разрешаются от каталога файла конфигурации; при вызове `createServer(config)` — от рабочего каталога. Данные в памяти, включая схему, копируются. Порт `0` позволяет системе выбрать свободный порт.
|
|
74
81
|
|
|
75
82
|
### CLI
|
|
76
83
|
|
|
77
|
-
| Флаг
|
|
84
|
+
| Флаг | Действие |
|
|
78
85
|
|---|---|
|
|
79
|
-
| `--
|
|
80
|
-
| `--
|
|
81
|
-
| `--soft-delete` | Включить мягкое удаление глобально |
|
|
82
|
-
| `--graphql` | Включить GraphQL API |
|
|
83
|
-
| `--openapi` | Включить HTTP-маршрут OpenAPI |
|
|
86
|
+
| `--generate` | Экспортировать схемы и запустить сервер |
|
|
87
|
+
| `--generate-only` | Экспортировать схемы и завершить работу |
|
|
84
88
|
| `--host <host>` | Адрес сервера |
|
|
85
89
|
| `--port <port>` | Порт сервера |
|
|
86
|
-
| `--help
|
|
87
|
-
| `--version
|
|
90
|
+
| `--help, -h` | Справка |
|
|
91
|
+
| `--version, -v` | Версия пакета |
|
|
88
92
|
|
|
89
|
-
Приоритет адреса и порта: CLI → конфигурация → `HOST`/`PORT` → значения по умолчанию.
|
|
93
|
+
Приоритет адреса и порта: CLI → конфигурация → `HOST`/`PORT` → значения по умолчанию. Без флагов генерации запускается только сервер. `--generate` и `--generate-only` нельзя передавать вместе.
|
|
90
94
|
|
|
91
95
|
## Схема моделей
|
|
92
96
|
|
|
@@ -94,28 +98,52 @@ npx deep-json-server server.config.js
|
|
|
94
98
|
|
|
95
99
|
```json
|
|
96
100
|
{
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
"api": [
|
|
102
|
+
"openapi",
|
|
103
|
+
"graphql"
|
|
104
|
+
],
|
|
105
|
+
"models": {
|
|
106
|
+
"Country": {
|
|
107
|
+
"collection": "countries",
|
|
108
|
+
"fields": {
|
|
109
|
+
"id": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"primary": true,
|
|
112
|
+
"generated": "uuid"
|
|
113
|
+
},
|
|
114
|
+
"name": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"required": true
|
|
117
|
+
},
|
|
118
|
+
"users": {
|
|
119
|
+
"type": "User[]",
|
|
120
|
+
"target": "countryId"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"User": {
|
|
125
|
+
"collection": "users",
|
|
126
|
+
"fields": {
|
|
127
|
+
"id": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"primary": true,
|
|
130
|
+
"generated": "uuid"
|
|
131
|
+
},
|
|
132
|
+
"fullName": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"required": true
|
|
135
|
+
},
|
|
136
|
+
"country": {
|
|
137
|
+
"type": "Country",
|
|
138
|
+
"source": "countryId"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
113
141
|
}
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
```
|
|
117
145
|
|
|
118
|
-
|
|
146
|
+
Модели находятся в `models`. В корне схемы можно задать `api`, `timestamps` и `softDelete`; у модели эти параметры переопределяют общие значения. Для `api` приоритет такой: модель → корень схемы → секции конфигурации. Массив заменяется целиком; `[]` исключает модель из GraphQL и OpenAPI, но REST продолжает работать. Явный список не включает отсутствующий модуль. Связанные модели должны разрешать тот же формат. Имена моделей должны быть допустимыми идентификаторами; конфликты типов и операций вызывают ошибку. Имена `and`, `or`, `not` зарезервированы фильтрами.
|
|
119
147
|
|
|
120
148
|
| Возможность | Со схемой | Без схемы |
|
|
121
149
|
|---|---|---|
|
|
@@ -126,7 +154,7 @@ npx deep-json-server server.config.js
|
|
|
126
154
|
|
|
127
155
|
При явной схеме неизвестные поля и коллекции запрещены. Исключение — хранимые ключи, выведенные из связей. Исходная база проверяется при загрузке. Генерация использует описание моделей.
|
|
128
156
|
|
|
129
|
-
REST без схемы создаёт ключ `id` и сохраняет произвольные JSON-поля. Для фильтров и выбора отдельных полей используются имена в формате
|
|
157
|
+
REST без схемы создаёт ключ `id` и сохраняет произвольные JSON-поля. Для фильтров и выбора отдельных полей используются имена в формате идентификаторов. `scope=[{"*":true}]` возвращает скаляры верхнего уровня; массивы и объекты выбираются по имени. Поля с разными типами значений можно запросить явно, но фильтрация, сортировка и пагинация неоднородных списков требуют явной схемы.
|
|
130
158
|
|
|
131
159
|
У модели обязательны `collection` — имя коллекции в базе и REST-пути — и `fields` — описание полей. Имя модели (`User`) задаёт имена типов и операций GraphQL. `api` управляет доступностью форматов, а `timestamps` и `softDelete` переопределяют глобальные настройки для этой модели.
|
|
132
160
|
|
|
@@ -184,6 +212,110 @@ REST без схемы создаёт ключ `id` и сохраняет про
|
|
|
184
212
|
|
|
185
213
|
Каскадное удаление выполняется целиком, включая циклические связи. Ошибка проверки отменяет всю операцию. Правила `onDelete` действуют и на явно объявленные обратные связи; при описании обоих направлений учитывайте оба правила.
|
|
186
214
|
|
|
215
|
+
## Пример базы данных
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"countries": [
|
|
220
|
+
{
|
|
221
|
+
"id": "1",
|
|
222
|
+
"isArchived": false,
|
|
223
|
+
"name": "Ардения"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": "2",
|
|
227
|
+
"isArchived": false,
|
|
228
|
+
"name": "Велория"
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
"genres": [
|
|
232
|
+
{
|
|
233
|
+
"id": "1",
|
|
234
|
+
"isArchived": false,
|
|
235
|
+
"name": "Криминал",
|
|
236
|
+
"parentIds": []
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "2",
|
|
240
|
+
"isArchived": false,
|
|
241
|
+
"name": "Гангстер",
|
|
242
|
+
"parentIds": ["1"]
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"id": "3",
|
|
246
|
+
"isArchived": false,
|
|
247
|
+
"name": "Драма",
|
|
248
|
+
"parentIds": []
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "4",
|
|
252
|
+
"isArchived": false,
|
|
253
|
+
"name": "Комедия",
|
|
254
|
+
"parentIds": []
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
"movies": [
|
|
258
|
+
{
|
|
259
|
+
"actors": [
|
|
260
|
+
{
|
|
261
|
+
"genreIds": ["2", "3"],
|
|
262
|
+
"id": "movie-1-actor-1",
|
|
263
|
+
"userId": "1"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"genreIds": ["3"],
|
|
267
|
+
"id": "movie-1-actor-2",
|
|
268
|
+
"userId": "2"
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"coverSrc": "https://example.com/covers/shadows-of-ardenia.jpg",
|
|
272
|
+
"description": "Наследница портового города раскрывает заговор двух соперничающих семей.",
|
|
273
|
+
"id": "1",
|
|
274
|
+
"isArchived": false,
|
|
275
|
+
"publisherIds": ["2"],
|
|
276
|
+
"title": "Тени Ардении"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"actors": [],
|
|
280
|
+
"coverSrc": "https://example.com/covers/northern-star.jpg",
|
|
281
|
+
"description": "Ночной администратор старого отеля случайно становится участником поисков пропавшей картины.",
|
|
282
|
+
"id": "2",
|
|
283
|
+
"isArchived": false,
|
|
284
|
+
"publisherIds": ["1"],
|
|
285
|
+
"title": "Полночь в «Северной звезде»"
|
|
286
|
+
}
|
|
287
|
+
],
|
|
288
|
+
"publishers": [
|
|
289
|
+
{
|
|
290
|
+
"id": "1",
|
|
291
|
+
"isArchived": false,
|
|
292
|
+
"name": "Northlight Studio"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"id": "2",
|
|
296
|
+
"isArchived": false,
|
|
297
|
+
"name": "Aurora Pictures"
|
|
298
|
+
}
|
|
299
|
+
],
|
|
300
|
+
"users": [
|
|
301
|
+
{
|
|
302
|
+
"bornAt": "1988-03-14",
|
|
303
|
+
"countryId": "1",
|
|
304
|
+
"fullName": "Мира Волкова",
|
|
305
|
+
"id": "1",
|
|
306
|
+
"isArchived": false
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"bornAt": "1991-11-02",
|
|
310
|
+
"countryId": "2",
|
|
311
|
+
"fullName": "Леон Ветров",
|
|
312
|
+
"id": "2",
|
|
313
|
+
"isArchived": false
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
187
319
|
## Запросы и ответы
|
|
188
320
|
|
|
189
321
|
Примеры с фильмами, актёрами и жанрами используют полную [схему из examples](examples/schema.json). Для их запуска используйте [конфигурацию примера](examples/server.config.js):
|
|
@@ -252,10 +384,7 @@ const scope = [
|
|
|
252
384
|
fullName: true,
|
|
253
385
|
movies: [
|
|
254
386
|
{ id: true, title: true },
|
|
255
|
-
{
|
|
256
|
-
order: [{ field: 'title', direction: 'ASC' }],
|
|
257
|
-
pager: { page: 1, pageSize: 5 },
|
|
258
|
-
},
|
|
387
|
+
{ order: [{ field: 'title', direction: 'ASC' }], pager: { page: 1, pageSize: 5 } },
|
|
259
388
|
],
|
|
260
389
|
},
|
|
261
390
|
{
|
|
@@ -268,7 +397,7 @@ const params = new URLSearchParams({ scope: JSON.stringify(scope) });
|
|
|
268
397
|
const response = await fetch(`/users?${params}`);
|
|
269
398
|
```
|
|
270
399
|
|
|
271
|
-
|
|
400
|
+
Скаляры и массивы примитивов выбираются через `true`, объекты и связи — через свой массив `scope`. Если аргументы не нужны, в массиве остаётся только объект полей. `"*": true` включает только скалярные поля. Массивы, объекты, связи и поля `writeOnly` в него не входят.
|
|
272
401
|
|
|
273
402
|
Например, собственные поля фильма, пользователи актёров и отсортированные жанры:
|
|
274
403
|
|
|
@@ -289,9 +418,9 @@ const response = await fetch(`/users?${params}`);
|
|
|
289
418
|
]
|
|
290
419
|
```
|
|
291
420
|
|
|
292
|
-
Без `scope` возвращаются
|
|
421
|
+
Без `scope` возвращаются скалярные поля, как при `[{"*":true}]`. Пустой выбор `[{}]` возвращает объект без полей. Списки сохраняют структуру `{ data, total }`.
|
|
293
422
|
|
|
294
|
-
Аргументы доступны только у списков. В запросе отдельной записи и в ответе мутации
|
|
423
|
+
Аргументы доступны только у списков. Вместо них список может использовать `{ "union": [scope, ...] }`: каждая часть — обычный scope списка, части выполняются в порядке массива, а для каждого первичного ключа остаётся первая запись. Это работает и у вложенных списков. В запросе отдельной записи и в ответе мутации аргументы можно задать для вложенных списков. Параметры проверяются даже на пустых данных; ошибка в выборе ответа отменяет изменения записи. Некорректный `scope` возвращает `400`. Максимальная длина JSON — 10 000 символов, глубина выбора — 32 уровня.
|
|
295
424
|
|
|
296
425
|
### Вложенная запись
|
|
297
426
|
|
|
@@ -346,10 +475,10 @@ mutation {
|
|
|
346
475
|
|
|
347
476
|
### GraphQL
|
|
348
477
|
|
|
349
|
-
Укажите `database.schema` и
|
|
478
|
+
Укажите `database.schema` и добавьте секцию `graphql: {}` в конфигурацию:
|
|
350
479
|
|
|
351
480
|
```sh
|
|
352
|
-
npx deep-json-server
|
|
481
|
+
npx deep-json-server server.config.js
|
|
353
482
|
```
|
|
354
483
|
|
|
355
484
|
Отправляйте запросы на `/graphql` методом POST с `Content-Type: application/json` и телом `{ "query": "…", "variables": {} }`. Путь можно изменить через `graphql.endpoint`.
|
|
@@ -386,29 +515,27 @@ query {
|
|
|
386
515
|
|
|
387
516
|
## OpenAPI и экспорт схем
|
|
388
517
|
|
|
389
|
-
Экспорт использует OpenAPI 3.0.3.
|
|
390
|
-
|
|
391
|
-
Для получения спецификации по HTTP укажите `database.schema` и включите `openapi.enabled: true` или запустите сервер с `--openapi`. По умолчанию JSON доступен по адресу `/openapi.json`; путь задаётся в `openapi.endpoint`. Спецификацию можно открыть в отдельно установленном Swagger UI или импортировать в API-клиент.
|
|
392
|
-
|
|
393
|
-
Для генерации укажите формат и файл конфигурации:
|
|
394
|
-
|
|
395
|
-
```sh
|
|
396
|
-
npx deep-json-server generate openapi server.config.js
|
|
397
|
-
npx deep-json-server generate graphql server.config.js
|
|
398
|
-
npx deep-json-server generate openapi,graphql server.config.js
|
|
399
|
-
```
|
|
518
|
+
Экспорт использует OpenAPI 3.0.3. Добавьте `openapi: {}` и `database.schema`, чтобы получать спецификацию по HTTP на `/openapi.json`. Путь меняется через `openapi.endpoint`. Спецификацию можно открыть в Swagger UI или импортировать в API-клиент.
|
|
400
519
|
|
|
401
|
-
|
|
520
|
+
Для сохранения схем задайте пути экспорта:
|
|
402
521
|
|
|
403
522
|
```js
|
|
404
523
|
export default {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
524
|
+
storage: 'file',
|
|
525
|
+
database: { source: './database.json', schema: './schema.json' },
|
|
526
|
+
openapi: { target: './generated/openapi.yaml' },
|
|
527
|
+
graphql: { target: './generated/schema.graphql' },
|
|
408
528
|
};
|
|
409
529
|
```
|
|
410
530
|
|
|
411
|
-
|
|
531
|
+
```bash
|
|
532
|
+
npx deep-json-server server.config.js --generate-only
|
|
533
|
+
npx deep-json-server server.config.js --generate
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
`--generate-only` экспортирует и завершает работу, `--generate` после экспорта запускает сервер. Форматы определяются наличием секций. Для каждого выбранного формата обязателен свой `target`. Если секций нет, отсутствует `target` или генерация завершилась ошибкой, команда возвращает ошибку и сервер не запускается.
|
|
537
|
+
|
|
538
|
+
Экспорт не открывает базу, учётные записи и файлы. Перед записью проверяются все выбранные `target`; они не могут совпадать с конфигурацией, базой, схемой, пользователями, счётчиками или метаданными файлов.
|
|
412
539
|
|
|
413
540
|
## Аутентификация
|
|
414
541
|
|
|
@@ -422,23 +549,30 @@ import { hashPassword } from '@kollors/deep-json-server/auth';
|
|
|
422
549
|
|
|
423
550
|
const password = process.env.DJS_PASSWORD;
|
|
424
551
|
if (!password) throw new Error('Set DJS_PASSWORD');
|
|
425
|
-
await writeFile(
|
|
426
|
-
|
|
427
|
-
|
|
552
|
+
await writeFile(
|
|
553
|
+
'./auth.json',
|
|
554
|
+
JSON.stringify(
|
|
555
|
+
[{ id: '1', username: 'admin', passwordHash: await hashPassword(password), isAdmin: true }],
|
|
556
|
+
null,
|
|
557
|
+
2,
|
|
558
|
+
),
|
|
559
|
+
{ flag: 'wx', mode: 0o600 },
|
|
560
|
+
);
|
|
428
561
|
```
|
|
429
562
|
|
|
430
563
|
Задайте `DJS_PASSWORD` и выполните `node setup-auth.mjs`. Добавьте файл в конфигурацию сервера:
|
|
431
564
|
|
|
432
565
|
```js
|
|
433
566
|
export default {
|
|
434
|
-
|
|
435
|
-
|
|
567
|
+
storage: 'file',
|
|
568
|
+
database: { source: './database.json' },
|
|
569
|
+
auth: { source: './auth.json', expiresIn: 3600 },
|
|
436
570
|
};
|
|
437
571
|
```
|
|
438
572
|
|
|
439
573
|
Запустите `npx deep-json-server server.config.js`. Каждой исходной учётной записи нужны уникальный строковый `id`, уникальный `username` и `passwordHash`, созданный функцией выше. `isAdmin` по умолчанию равен `false`. Пароли хешируются через scrypt со случайной солью.
|
|
440
574
|
|
|
441
|
-
|
|
575
|
+
При `storage: 'memory'` передайте массив в `auth.source`:
|
|
442
576
|
|
|
443
577
|
```js
|
|
444
578
|
import { hashPassword } from '@kollors/deep-json-server/auth';
|
|
@@ -447,16 +581,17 @@ const password = process.env.DJS_PASSWORD;
|
|
|
447
581
|
if (!password) throw new Error('Set DJS_PASSWORD');
|
|
448
582
|
|
|
449
583
|
export default {
|
|
450
|
-
|
|
584
|
+
storage: 'memory',
|
|
585
|
+
database: { source: { items: [] } },
|
|
451
586
|
auth: {
|
|
452
|
-
|
|
587
|
+
source: [
|
|
453
588
|
{ id: '1', username: 'admin', passwordHash: await hashPassword(password), isAdmin: true },
|
|
454
589
|
],
|
|
455
590
|
},
|
|
456
591
|
};
|
|
457
592
|
```
|
|
458
593
|
|
|
459
|
-
Учётные записи auth хранятся отдельно от
|
|
594
|
+
Учётные записи auth хранятся отдельно от базы. В файловом режиме изменения сохраняются в `auth.source`; в режиме памяти они исчезают после перезапуска. Переданный массив пользователей не изменяется. После ручного изменения файла перезапустите сервер.
|
|
460
595
|
|
|
461
596
|
| REST-запрос | JSON тела | Ответ |
|
|
462
597
|
|---|---|---|
|
|
@@ -475,40 +610,70 @@ export default {
|
|
|
475
610
|
|
|
476
611
|
Недействительный или истёкший токен возвращает `401`, недостаток прав — `403`, отсутствующий пользователь при разрешённой операции — `404`. Ошибки тела запроса возвращают `400`. Вход, регистрация и смена пароля могут вернуть `429` при превышении числа одновременных вычислений паролей; вход также ограничивает число активных сессий. Сессии хранятся в памяти и исчезают после перезапуска. Выход завершает только сессию переданного токена.
|
|
477
612
|
|
|
478
|
-
|
|
613
|
+
При включённом auth у каждой записи модели доступен виртуальный объект `actions`. Он вычисляется для текущего пользователя и не сохраняется в базе. В REST его нужно явно запросить через `scope`:
|
|
479
614
|
|
|
480
|
-
|
|
615
|
+
```json
|
|
616
|
+
[
|
|
617
|
+
{
|
|
618
|
+
"id": true,
|
|
619
|
+
"actions": [{ "*": true }]
|
|
620
|
+
}
|
|
621
|
+
]
|
|
622
|
+
```
|
|
481
623
|
|
|
482
|
-
|
|
624
|
+
Объект содержит `update`, `replace` и `delete`. Владелец или администратор получает `true`, а анонимный или посторонний пользователь — `false`. Записи без `createdById` изменяет только администратор. Без токена чтение остаётся открытым, а флаги равны `false`. Переданный недействительный токен возвращает `401` в REST или `UNAUTHENTICATED` в GraphQL.
|
|
483
625
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
626
|
+
В GraphQL то же поле выбирается обычным способом:
|
|
627
|
+
|
|
628
|
+
```graphql
|
|
629
|
+
query {
|
|
630
|
+
itemList {
|
|
631
|
+
data {
|
|
632
|
+
id
|
|
633
|
+
actions {
|
|
634
|
+
update
|
|
635
|
+
replace
|
|
636
|
+
delete
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
494
641
|
```
|
|
495
642
|
|
|
496
|
-
|
|
643
|
+
`actions` доступен у корневых и связанных записей моделей, включая результаты мутаций. Его нельзя записывать, фильтровать или сортировать. Обычные вложенные объекты и ответы методов auth это поле не получают.
|
|
644
|
+
|
|
645
|
+
OpenAPI описывает маршруты auth, необязательную Bearer-аутентификацию при чтении записей, обязательную при изменениях и поле ответа `actions`. В Swagger UI токен из ответа на вход можно вставить в **Authorize**. Для экспорта схем включите auth в конфигурации и выполните `npx deep-json-server server.config.js --generate-only`; файл учётных записей при генерации не читается. Методы auth доступны через REST. В GraphQL тот же токен проверяется при чтении прав и изменении записей. Для GraphQL и OpenAPI нужна `database.schema`.
|
|
646
|
+
|
|
647
|
+
## Даты записей, удаление и владельцы
|
|
648
|
+
|
|
649
|
+
Общие настройки задаются в корне схемы. В этом примере модель `Note` наследует мягкое удаление и отключает даты:
|
|
497
650
|
|
|
498
651
|
```json
|
|
499
652
|
{
|
|
500
|
-
"
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
"
|
|
504
|
-
|
|
505
|
-
"
|
|
506
|
-
"
|
|
653
|
+
"timestamps": true,
|
|
654
|
+
"softDelete": true,
|
|
655
|
+
"models": {
|
|
656
|
+
"Note": {
|
|
657
|
+
"collection": "notes",
|
|
658
|
+
"timestamps": false,
|
|
659
|
+
"fields": {
|
|
660
|
+
"id": {
|
|
661
|
+
"type": "string",
|
|
662
|
+
"primary": true,
|
|
663
|
+
"generated": "uuid"
|
|
664
|
+
},
|
|
665
|
+
"text": {
|
|
666
|
+
"type": "string",
|
|
667
|
+
"required": true
|
|
668
|
+
}
|
|
669
|
+
}
|
|
507
670
|
}
|
|
508
671
|
}
|
|
509
672
|
}
|
|
510
673
|
```
|
|
511
674
|
|
|
675
|
+
Приоритет: модель → корень схемы → `false`. Явное `false` отключает унаследованную настройку. Без схемы даты и мягкое удаление выключены.
|
|
676
|
+
|
|
512
677
|
| Поле | Когда включено | Значение |
|
|
513
678
|
|---|---|---|
|
|
514
679
|
| `createdAt`, `updatedAt` | `timestamps` | Даты создания и последнего изменения |
|
|
@@ -545,8 +710,9 @@ REST возвращает 401 при отсутствии действитель
|
|
|
545
710
|
|
|
546
711
|
```js
|
|
547
712
|
export default {
|
|
548
|
-
|
|
549
|
-
|
|
713
|
+
storage: 'file',
|
|
714
|
+
database: { source: './database.json' },
|
|
715
|
+
files: { source: './uploads' },
|
|
550
716
|
};
|
|
551
717
|
```
|
|
552
718
|
|
|
@@ -556,7 +722,7 @@ export default {
|
|
|
556
722
|
npx deep-json-server server.config.js
|
|
557
723
|
```
|
|
558
724
|
|
|
559
|
-
Для временных тестов
|
|
725
|
+
Для временных тестов выберите `storage: 'memory'` и передайте массив в `files.source`. Каждая начальная запись содержит `name`, `mimeType`, бинарное `content` в виде `Uint8Array` и необязательный `directory`. Загруженные файлы в таком режиме остаются в памяти до завершения процесса.
|
|
560
726
|
|
|
561
727
|
Один файл отправляется непосредственно в теле запроса. `Content-Name` содержит URI-кодированное имя файла, `Content-Type` — его MIME-тип, а необязательный `Content-Directory` — URI-кодированный относительный путь к директории:
|
|
562
728
|
|
|
@@ -607,9 +773,9 @@ Content-Type: application/json
|
|
|
607
773
|
}
|
|
608
774
|
```
|
|
609
775
|
|
|
610
|
-
`PATCH` возвращает обновлённые метаданные со статусом `200`; если по новому пути уже существует файл, сервер возвращает `409`. `DELETE` отвечает статусом `204` без тела. Если файл не найден, любая операция по пути возвращает `404`. Пути в URL задаются относительно `files.
|
|
776
|
+
`PATCH` возвращает обновлённые метаданные со статусом `200`; если по новому пути уже существует файл, сервер возвращает `409`. `DELETE` отвечает статусом `204` без тела. Если файл не найден, любая операция по пути возвращает `404`. Пути в URL задаются относительно `files.source`, а все возвращаемые URL — относительно адреса сервера.
|
|
611
777
|
|
|
612
|
-
При хранении на диске бинарный файл находится по пути `<files.
|
|
778
|
+
При хранении на диске бинарный файл находится по пути `<files.source>/<directory>/<name>`. Метаданные по умолчанию хранятся в `<files.source>/.files.json`; другой путь можно задать через `files.metadata`. Директории и файл метаданных создаются по мере необходимости.
|
|
613
779
|
|
|
614
780
|
Используйте один процесс сервера для дисковой базы и файлового хранилища. Перед ручным изменением файлов или метаданных остановите его. Пути в хранилище не могут содержать символические ссылки. Загрузка и переименование не могут перезаписать базу, счётчики, схему, учётные записи auth, загруженную конфигурацию или файл метаданных.
|
|
615
781
|
|
|
@@ -629,7 +795,7 @@ await server.listen();
|
|
|
629
795
|
|
|
630
796
|
Методы `openapi()` и `graphql()` возвращают схемы и требуют `database.schema`. `fastify()` возвращает экземпляр сервера для настройки и запуска. База и включённые сервисы инициализируются при `ready()`, `listen()` или первом `inject()`; ошибка инициализации останавливает запуск.
|
|
631
797
|
|
|
632
|
-
|
|
798
|
+
`createServer(config)` принимает один аргумент. Наличие секций управляет модулями так же, как при запуске через CLI. Для методов `openapi()` и `graphql()` нужна соответствующая секция. Методы возвращают схему и не записывают файлы.
|
|
633
799
|
|
|
634
800
|
Эти функции доступны и через общий импорт `@kollors/deep-json-server`. Адаптеры сервера загружаются при включении. Генераторы можно использовать отдельно:
|
|
635
801
|
|
|
@@ -643,114 +809,10 @@ await writeOpenapi(document, './generated/openapi.yaml');
|
|
|
643
809
|
await writeGraphql(sdl, './generated/schema.graphql');
|
|
644
810
|
```
|
|
645
811
|
|
|
646
|
-
|
|
812
|
+
Отдельные генераторы принимают путь или объект схемы и не требуют конфигурации сервера. Выбранная функция задаёт формат по умолчанию; `api` в схеме и моделях может его ограничить. `timestamps` и `softDelete` берутся из схемы. Опция `{ auth: true }` добавляет поля владельца и `actions`; OpenAPI также описывает маршруты auth и требования токена. `hashPassword()` доступна и через общий импорт пакета.
|
|
647
813
|
|
|
648
814
|
`generateOpenapi()` также принимает `host`, `port`, `pageSize`, `maxPageSize` и `info`. Вместо пути можно передать объект схемы. Сервер и генераторы работают с собственной копией модели. Размеры страниц должны быть положительными целыми числами; `pageSize` не может превышать `maxPageSize`.
|
|
649
815
|
|
|
650
|
-
## Пример базы данных
|
|
651
|
-
|
|
652
|
-
```json
|
|
653
|
-
{
|
|
654
|
-
"countries": [
|
|
655
|
-
{
|
|
656
|
-
"id": "1",
|
|
657
|
-
"isArchived": false,
|
|
658
|
-
"name": "Ардения"
|
|
659
|
-
},
|
|
660
|
-
{
|
|
661
|
-
"id": "2",
|
|
662
|
-
"isArchived": false,
|
|
663
|
-
"name": "Велория"
|
|
664
|
-
}
|
|
665
|
-
],
|
|
666
|
-
"genres": [
|
|
667
|
-
{
|
|
668
|
-
"id": "1",
|
|
669
|
-
"isArchived": false,
|
|
670
|
-
"name": "Криминал",
|
|
671
|
-
"parentIds": []
|
|
672
|
-
},
|
|
673
|
-
{
|
|
674
|
-
"id": "2",
|
|
675
|
-
"isArchived": false,
|
|
676
|
-
"name": "Гангстер",
|
|
677
|
-
"parentIds": ["1"]
|
|
678
|
-
},
|
|
679
|
-
{
|
|
680
|
-
"id": "3",
|
|
681
|
-
"isArchived": false,
|
|
682
|
-
"name": "Драма",
|
|
683
|
-
"parentIds": []
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
"id": "4",
|
|
687
|
-
"isArchived": false,
|
|
688
|
-
"name": "Комедия",
|
|
689
|
-
"parentIds": []
|
|
690
|
-
}
|
|
691
|
-
],
|
|
692
|
-
"movies": [
|
|
693
|
-
{
|
|
694
|
-
"actors": [
|
|
695
|
-
{
|
|
696
|
-
"genreIds": ["2", "3"],
|
|
697
|
-
"id": "movie-1-actor-1",
|
|
698
|
-
"userId": "1"
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
"genreIds": ["3"],
|
|
702
|
-
"id": "movie-1-actor-2",
|
|
703
|
-
"userId": "2"
|
|
704
|
-
}
|
|
705
|
-
],
|
|
706
|
-
"coverSrc": "https://example.com/covers/shadows-of-ardenia.jpg",
|
|
707
|
-
"description": "Наследница портового города раскрывает заговор двух соперничающих семей.",
|
|
708
|
-
"id": "1",
|
|
709
|
-
"isArchived": false,
|
|
710
|
-
"publisherIds": ["2"],
|
|
711
|
-
"title": "Тени Ардении"
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
"actors": [],
|
|
715
|
-
"coverSrc": "https://example.com/covers/northern-star.jpg",
|
|
716
|
-
"description": "Ночной администратор старого отеля случайно становится участником поисков пропавшей картины.",
|
|
717
|
-
"id": "2",
|
|
718
|
-
"isArchived": false,
|
|
719
|
-
"publisherIds": ["1"],
|
|
720
|
-
"title": "Полночь в «Северной звезде»"
|
|
721
|
-
}
|
|
722
|
-
],
|
|
723
|
-
"publishers": [
|
|
724
|
-
{
|
|
725
|
-
"id": "1",
|
|
726
|
-
"isArchived": false,
|
|
727
|
-
"name": "Northlight Studio"
|
|
728
|
-
},
|
|
729
|
-
{
|
|
730
|
-
"id": "2",
|
|
731
|
-
"isArchived": false,
|
|
732
|
-
"name": "Aurora Pictures"
|
|
733
|
-
}
|
|
734
|
-
],
|
|
735
|
-
"users": [
|
|
736
|
-
{
|
|
737
|
-
"bornAt": "1988-03-14",
|
|
738
|
-
"countryId": "1",
|
|
739
|
-
"fullName": "Мира Волкова",
|
|
740
|
-
"id": "1",
|
|
741
|
-
"isArchived": false
|
|
742
|
-
},
|
|
743
|
-
{
|
|
744
|
-
"bornAt": "1991-11-02",
|
|
745
|
-
"countryId": "2",
|
|
746
|
-
"fullName": "Леон Ветров",
|
|
747
|
-
"id": "2",
|
|
748
|
-
"isArchived": false
|
|
749
|
-
}
|
|
750
|
-
]
|
|
751
|
-
}
|
|
752
|
-
```
|
|
753
|
-
|
|
754
816
|
## Хранение данных
|
|
755
817
|
|
|
756
818
|
Изменения выполняются последовательно внутри экземпляра сервера и проверяются на копии данных до сохранения. Для одного файла базы используйте один процесс сервера. Счётчики `increment` хранятся рядом с базой в `<путь базы>.counters.json`; сохраняйте этот файл вместе с базой. Номера резервируются до записи данных: сбой может оставить пропуск, но не приводит к повторной выдаче номера.
|
|
@@ -766,6 +828,6 @@ npm run verify
|
|
|
766
828
|
|
|
767
829
|
Команда проверяет типы, стиль кода, покрытие тестами и установку пакета из архива.
|
|
768
830
|
|
|
769
|
-
Для публикации
|
|
831
|
+
Для публикации предварительной версии обновите номер в `package.json`, `package-lock.json` и `src/core/constants.ts`, затем отправьте коммит в `main`. GitHub Actions создаст тег `v<версия>` и опубликует пакет через trusted publishing в канал `alpha`, `beta` или `rc`. Стабильные версии публикуются в `latest` из явно отправленного тега версии.
|
|
770
832
|
|
|
771
833
|
Лицензия: MIT.
|