@kollors/deep-json-server 1.0.0-rc.8 → 1.0.0
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/CHANGELOG.md +21 -4
- package/MIGRATION.md +26 -18
- package/MIGRATION.ru.md +36 -28
- package/README.md +91 -44
- package/README.ru.md +119 -72
- package/dist/package.json +4 -3
- package/package.json +4 -3
package/README.ru.md
CHANGED
|
@@ -4,21 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
[История выпусков](CHANGELOG.md) · [Переход с 0.9.0](MIGRATION.ru.md)
|
|
6
6
|
|
|
7
|
-
JSON-сервер для
|
|
8
|
-
|
|
9
|
-
**Изменения в 1.0.0, нарушающие совместимость.** При переходе с 0.9.0 воспользуйтесь [инструкцией по миграции](MIGRATION.ru.md). `*` в REST `scope` выбирает только скалярные поля, которые не хранят ключи связей. Массивы, объекты, связи и явно объявленные ключи связей нужно указывать в выборе полей. При включённой аутентификации права на запись доступны через виртуальное поле `actions`.
|
|
7
|
+
JSON-сервер для тестового API с REST, GraphQL, связями между записями, загрузкой файлов и экспортом схем. Поддерживает аутентификацию, права владельца и администратора, даты создания и изменения записей, а также мягкое удаление. Требуется Node.js 22 или новее.
|
|
10
8
|
|
|
11
9
|
## Установка
|
|
12
10
|
|
|
13
11
|
```sh
|
|
14
|
-
npm install @kollors/deep-json-server
|
|
12
|
+
npm install @kollors/deep-json-server
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Чтобы установить именно версию 1.0.0, выполните `npm install @kollors/deep-json-server@1.0.0`.
|
|
18
16
|
|
|
19
17
|
## Быстрый старт
|
|
20
18
|
|
|
21
|
-
Создайте два файла в одном каталоге.
|
|
19
|
+
Создайте два файла в одном каталоге. У конфигурации расширение `.mjs`, поскольку это ES-модуль. Если используете `.js`, добавьте `"type": "module"` в `package.json` проекта.
|
|
22
20
|
|
|
23
21
|
`database.json`:
|
|
24
22
|
|
|
@@ -30,14 +28,14 @@ npm install @kollors/deep-json-server@rc
|
|
|
30
28
|
}
|
|
31
29
|
```
|
|
32
30
|
|
|
33
|
-
`server.config.
|
|
31
|
+
`server.config.mjs`:
|
|
34
32
|
|
|
35
33
|
```js
|
|
36
34
|
export default { storage: 'file', database: { source: './database.json' } };
|
|
37
35
|
```
|
|
38
36
|
|
|
39
37
|
```sh
|
|
40
|
-
npx deep-json-server server.config.
|
|
38
|
+
npx deep-json-server server.config.mjs
|
|
41
39
|
```
|
|
42
40
|
|
|
43
41
|
Список пользователей доступен по адресу `http://127.0.0.1:4001/users`.
|
|
@@ -46,7 +44,9 @@ npx deep-json-server server.config.js
|
|
|
46
44
|
|
|
47
45
|
## Конфигурация
|
|
48
46
|
|
|
49
|
-
При `storage: 'file'`
|
|
47
|
+
При `storage: 'file'` укажите пути ко всем источникам данных и схеме, при `'memory'` — передайте сами данные. Режим хранения общий для всей конфигурации.
|
|
48
|
+
|
|
49
|
+
Секции `auth` и `files` включают соответствующие REST-маршруты. Секция `graphql` включает GraphQL, а `openapi` — HTTP-маршрут со спецификацией REST. Пустые секции `graphql: {}` и `openapi: {}` используют пути по умолчанию; для экспорта в файл добавьте `target`. При наличии секции `graphql` явно заданный корневой `api` должен содержать `graphql`. Если `api` не задан, сервер использует `['rest', 'graphql']`.
|
|
50
50
|
|
|
51
51
|
```js
|
|
52
52
|
export default {
|
|
@@ -64,25 +64,25 @@ export default {
|
|
|
64
64
|
| Настройка | Назначение |
|
|
65
65
|
|---|---|
|
|
66
66
|
| `storage` | Обязательный режим: `file` или `memory`; общий для всех источников и схемы |
|
|
67
|
-
| `database.source` | Путь к JSON базы или объект
|
|
68
|
-
| `database.schema` | Путь к JSON схемы или объект схемы;
|
|
69
|
-
| `auth.source` | Путь к JSON пользователей или массив пользователей |
|
|
70
|
-
| `auth.expiresIn` | Срок сессии в секундах; по умолчанию 3600 |
|
|
67
|
+
| `database.source` | Путь к JSON-файлу базы или объект с коллекциями |
|
|
68
|
+
| `database.schema` | Путь к JSON-файлу схемы или объект схемы; для REST можно не задавать |
|
|
69
|
+
| `auth.source` | Путь к JSON-файлу пользователей или массив пользователей |
|
|
70
|
+
| `auth.expiresIn` | Срок действия сессии в секундах; по умолчанию 3600 |
|
|
71
71
|
| `files.source` | Каталог файлов или массив начальных файлов |
|
|
72
|
-
| `files.metadata` | Только для `file`: путь JSON метаданных; по умолчанию `.files.json` внутри `files.source` |
|
|
72
|
+
| `files.metadata` | Только для `file`: путь к JSON-файлу метаданных; по умолчанию `.files.json` внутри `files.source` |
|
|
73
73
|
| `graphql.endpoint` | HTTP-маршрут; по умолчанию `/graphql` |
|
|
74
74
|
| `graphql.target` | Файл для экспорта GraphQL SDL |
|
|
75
75
|
| `openapi.endpoint` | HTTP-маршрут; по умолчанию `/openapi.json` |
|
|
76
76
|
| `openapi.target` | Файл для экспорта OpenAPI |
|
|
77
77
|
| `package.source` | `package.json` проекта; обязателен при наличии `openapi` |
|
|
78
78
|
| `server.host`, `server.port` | По умолчанию `127.0.0.1`, `4001`; CLI также читает `HOST`/`PORT` |
|
|
79
|
-
| `server.pageSize`, `server.maxPageSize` | По умолчанию 10 и 100;
|
|
79
|
+
| `server.pageSize`, `server.maxPageSize` | По умолчанию 10 и 100; если `pageSize` больше максимума, используется максимум |
|
|
80
80
|
| `server.cors`, `server.logger` | По умолчанию `true`; logger принимает также настройки Fastify |
|
|
81
81
|
| `server.maxFileSize` | По умолчанию 100 МиБ |
|
|
82
82
|
|
|
83
83
|
Формат `package.source` зависит от режима хранения. При `storage: 'file'` укажите путь к `package.json`. При `storage: 'memory'` передайте метаданные напрямую: `{ name: 'example-api', version: '1.0.0', description: 'Example API' }`; `name` и `version` обязательны, `description` необязателен.
|
|
84
84
|
|
|
85
|
-
Относительные пути
|
|
85
|
+
Относительные пути отсчитываются от каталога файла конфигурации; при вызове `createServer(config)` — от рабочего каталога. Сервер копирует переданные в память данные, включая схему и метаданные пакета. Порт `0` позволяет системе выбрать свободный порт.
|
|
86
86
|
|
|
87
87
|
Чтобы открыть базу только через GraphQL, а аутентификацию и файлы оставить в REST с описанием OpenAPI, укажите:
|
|
88
88
|
|
|
@@ -113,7 +113,7 @@ export default {
|
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
Для REST с описанием OpenAPI используйте `"api": ["rest"]` и `openapi: {}`; для обоих API базы — `["rest", "graphql"]` с обеими секциями.
|
|
116
|
+
Для REST с описанием OpenAPI используйте `"api": ["rest"]` и `openapi: {}`; для обоих API базы — `["rest", "graphql"]` с обеими секциями. Если корневой `api` задан явно, при наличии секции `graphql` в конфигурации добавьте `graphql` и в список API; без этой секции включать его в список нельзя. Если корневой `api` не задан, сервер определяет его по конфигурации. OpenAPI описывает маршруты базы, только если включён REST; маршруты аутентификации и файлов включаются при наличии соответствующих секций.
|
|
117
117
|
|
|
118
118
|
### CLI
|
|
119
119
|
|
|
@@ -188,9 +188,13 @@ export default {
|
|
|
188
188
|
|
|
189
189
|
Явный `['rest']` включает REST-маршруты базы; `['graphql']` — операции GraphQL; `['rest', 'graphql']` — оба API. OpenAPI описывает REST-маршруты при наличии секции `openapi`. Корневой `api: []` недопустим; `[]` разрешён только на уровне модели.
|
|
190
190
|
|
|
191
|
-
Модель без собственного `api` наследует значение из корня. Указанный у модели массив может содержать `['rest']`, `['graphql']`, оба варианта или `[]`. Модель не может включить API, отсутствующий в корневом `api`.
|
|
191
|
+
Модель без собственного `api` наследует значение из корня. Указанный у модели массив может содержать `['rest']`, `['graphql']`, оба варианта или `[]`. Модель не может включить API, отсутствующий в корневом `api`.
|
|
192
|
+
|
|
193
|
+
OpenAPI включает модели, доступные через REST. Для генерации GraphQL нужна хотя бы одна GraphQL-модель; для генерации OpenAPI с маршрутами базы — хотя бы одна REST-модель. Если база доступна только через GraphQL, OpenAPI всё равно может описывать настроенные маршруты аутентификации и файлов.
|
|
192
194
|
|
|
193
|
-
|
|
195
|
+
Связанные модели должны разрешать тот же API. Имена моделей должны быть допустимыми идентификаторами; конфликты типов и операций вызывают ошибку. Имена `and`, `or`, `not` зарезервированы фильтрами.
|
|
196
|
+
|
|
197
|
+
Порядок элементов в `api` не важен.
|
|
194
198
|
|
|
195
199
|
Например, при корневом `api: ['rest', 'graphql']` модель с `api: ['rest']` получает REST-маршруты и пути в OpenAPI без операций GraphQL, а модель с `api: ['graphql']` — операции GraphQL без REST-маршрутов и путей в OpenAPI.
|
|
196
200
|
|
|
@@ -218,21 +222,25 @@ REST без схемы создаёт ключ `id` и сохраняет про
|
|
|
218
222
|
| `type` | Обязательный тип |
|
|
219
223
|
| `description`, `example` | Документация и пример |
|
|
220
224
|
| `required`, `nullable` | По умолчанию `false`; наличие поля и разрешение `null` независимы |
|
|
221
|
-
| `default` | Значение при
|
|
225
|
+
| `default` | Значение для поля, не переданного при создании или замене; PATCH не подставляет его |
|
|
222
226
|
| `enum` | Допустимые строки, числа или логические значения; у массива — значения каждого элемента |
|
|
223
227
|
| `primary` | Корневой первичный ключ: обязательный, уникальный, неизменяемый, без `null` |
|
|
224
228
|
| `generated` | `uuid` для строк, `increment` для чисел; значение создаёт сервер |
|
|
225
229
|
| `readOnly`, `writeOnly` | Только ответ или только входные данные; взаимно исключаются |
|
|
226
230
|
| `minLength`, `maxLength`, `pattern` | Ограничения строк |
|
|
227
231
|
| `format` | `date`, `date-time`, `email`, `uri`, `uuid` |
|
|
228
|
-
| `minimum`, `maximum` |
|
|
232
|
+
| `minimum`, `maximum` | Нижняя и верхняя границы числа, включая сами границы |
|
|
229
233
|
| `keyOn`, `source`, `target`, `onDelete` | Описание связи |
|
|
230
234
|
|
|
231
|
-
Ограничения строк и чисел применяются к каждому элементу `string[]`/`number[]`. `required` и `nullable` относятся ко всему массиву; `default` и `example` содержат массив целиком. Элементы
|
|
235
|
+
Ограничения строк и чисел применяются к каждому элементу `string[]`/`number[]`. `required` и `nullable` относятся ко всему массиву; `default` и `example` содержат массив целиком. Элементы должны соответствовать типу массива и не могут быть `null`. При `required: true` поле массива должно присутствовать в записи, но сам массив может быть пустым.
|
|
232
236
|
|
|
233
237
|
Каждое значение `enum` должно соответствовать типу поля и его ограничениям. Чтобы разрешить `null` для поля, укажите `nullable: true`; добавлять `null` в `enum` не нужно. Некорректные значения отклоняются при загрузке схемы.
|
|
234
238
|
|
|
235
|
-
У модели ровно один первичный ключ типа `string` или `number`, объявленный на верхнем уровне. Имя произвольное: `id`, `username`, `code`. Если `generated` не задан, значение передаёт клиент при создании.
|
|
239
|
+
У модели ровно один первичный ключ типа `string` или `number`, объявленный на верхнем уровне. Имя произвольное: `id`, `username`, `code`. Если `generated` не задан, значение передаёт клиент при создании.
|
|
240
|
+
|
|
241
|
+
Генерируемые поля объявляются на верхнем уровне, исключаются из входных типов и несовместимы с `default`.
|
|
242
|
+
|
|
243
|
+
При замене записи сохраняются генерируемые значения и поля `readOnly`, в том числе во вложенных объектах. Даже при `nullable: true` объект нельзя заменить на `null`, если это удалит сохранённые поля `readOnly` внутри него. Для защиты полей внутри массива задайте `readOnly` всему массиву или содержащему его объекту. Объекты, состоящие только из полей, которыми управляет сервер, доступны лишь в ответах.
|
|
236
244
|
|
|
237
245
|
Например, `LocalUser` с первичным ключом `username` и полем `password: {"type":"string","required":true,"writeOnly":true}` получает запрос `localUser(username: ...)` и маршрут `/localUsers/{username}`. Поле `writeOnly` доступно для записи и исключено из ответов, `scope`, фильтров и сортировки.
|
|
238
246
|
|
|
@@ -257,9 +265,13 @@ REST без схемы создаёт ключ `id` и сохраняет про
|
|
|
257
265
|
|
|
258
266
|
`source` всегда указывает на поле текущей модели, а `target` — на поле модели из `type`. Для обратной связи пути меняются местами: у `Country.users` это `source: "id"` и `target: "countryId"`. Если ключ хранится под нестандартным именем, задайте путь явно. Когда между двумя моделями несколько связей, уточните обратные поля: например, `Country.birthUsers = {"type":"User[]","keyOn":"related","target":"birthCountryId"}` и `Country.residenceUsers = {"type":"User[]","keyOn":"related","target":"residenceCountryId"}`. Отсутствующая, противоречивая или неоднозначная пара вызывает ошибку при загрузке схемы.
|
|
259
267
|
|
|
260
|
-
Ключи связей остаются в базе, даже если их нет в `fields`. На стороне `current`
|
|
268
|
+
Ключи связей остаются в базе, даже если их нет в `fields`. На стороне `current` хранимый ключ, на который указывает `source`, можно не объявлять в `fields`, если известен тип целевого ключа. Сервер определит его тип: массив для множественной связи или одно значение для одиночной. Это действует и для первичного, и для непервичного целевого поля.
|
|
261
269
|
|
|
262
|
-
|
|
270
|
+
Такой ключ остаётся внутренним: REST и GraphQL не позволяют читать, выбирать, фильтровать, сортировать или записывать его напрямую, а OpenAPI его не описывает. Сама связь при этом доступна.
|
|
271
|
+
|
|
272
|
+
Чтобы открыть ключ в API, объявите его в `fields`, например `"countryId": { "type": "string" }`. При неоднозначном сопоставлении ключ тоже нужно объявить явно.
|
|
273
|
+
|
|
274
|
+
Например, для `publishers: { "type": "Publisher[]", "keyOn": "current", "target": "code" }` сервер определяет ключ `publisherCodes: { "type": "string[]" }`, если `Publisher.code` — строка, даже при первичном ключе `Publisher.id`. Типы явно объявленных хранимых ключей сохраняются.
|
|
263
275
|
|
|
264
276
|
Обратная связь: `User.movies = {"type":"Movie[]","keyOn":"related","target":"actors.userId"}` соответствует `Movie.actors.user = {"type":"User","keyOn":"current","source":"actors.userId"}`. Фильм возвращается один раз, даже если совпало несколько актёров. Несколько совпадений для одиночной связи — ошибка.
|
|
265
277
|
|
|
@@ -269,13 +281,15 @@ REST без схемы создаёт ключ `id` и сохраняет про
|
|
|
269
281
|
|
|
270
282
|
`onDelete` срабатывает **при удалении целевой записи**:
|
|
271
283
|
|
|
272
|
-
- `restrict` — по умолчанию: запретить удаление, пока на цель ссылается
|
|
284
|
+
- `restrict` — по умолчанию: запретить удаление, пока на цель ссылается запись, которая остаётся в базе.
|
|
273
285
|
- `cascade` — удалить ссылающуюся запись. Для `User.country` удаление страны удаляет пользователей. Для `Movie.actors.user` удаление пользователя удаляет соответствующие элементы `actors`, сохраняя фильм.
|
|
274
286
|
|
|
275
287
|
Каскадное удаление выполняется целиком, включая циклические связи. Ошибка проверки отменяет всю операцию. Обратная связь `related` без `onDelete` сама по себе не запрещает удаление записи из-за ключа на другой стороне; задайте `onDelete`, если для неё нужно дополнительное правило.
|
|
276
288
|
|
|
277
289
|
## Пример базы данных
|
|
278
290
|
|
|
291
|
+
Эта база использует полную [схему примера](examples/schema.json). Кроме пользователей и стран из сокращённой схемы выше, она содержит фильмы, жанры и издателей.
|
|
292
|
+
|
|
279
293
|
```json
|
|
280
294
|
{
|
|
281
295
|
"countries": [
|
|
@@ -368,7 +382,7 @@ REST без схемы создаёт ключ `id` и сохраняет про
|
|
|
368
382
|
|
|
369
383
|
## Запросы и ответы
|
|
370
384
|
|
|
371
|
-
|
|
385
|
+
Запустите примеры ниже с [конфигурацией примера](examples/server.config.js):
|
|
372
386
|
|
|
373
387
|
```sh
|
|
374
388
|
npx deep-json-server examples/server.config.js
|
|
@@ -384,7 +398,9 @@ npx deep-json-server examples/server.config.js
|
|
|
384
398
|
|
|
385
399
|
В `pager` задаются `page` и `pageSize`. По умолчанию возвращается первая страница с размером из `server.pageSize`. Оба значения должны быть положительными целыми числами; `pageSize` ограничен `server.maxPageSize`. За пределами списка возвращается пустой `data` с общим числом найденных записей в `total`.
|
|
386
400
|
|
|
387
|
-
Фильтры: `eq`, `ne`, `in`; для строк `contains`, `startsWith`, `endsWith`; сравнения `gt`, `gte`, `lt`, `lte`. Несколько условий в одном объекте должны выполняться одновременно. `and` и `or` принимают массив условий, `not` — одно условие; `not` также можно использовать внутри фильтра поля. У массивов есть `some`, `every`, `none`; у примитивных массивов также `contains`, `in`.
|
|
401
|
+
Фильтры: `eq`, `ne`, `in`; для строк `contains`, `startsWith`, `endsWith`; сравнения `gt`, `gte`, `lt`, `lte`. Несколько условий в одном объекте должны выполняться одновременно. `and` и `or` принимают массив условий, `not` — одно условие; `not` также можно использовать внутри фильтра поля. У массивов есть `some`, `every`, `none`; у примитивных массивов также `contains`, `in`.
|
|
402
|
+
|
|
403
|
+
`contains`, `startsWith` и `endsWith` для строк не учитывают регистр; `eq`, `ne` и `in` сравнивают точные значения. Для строк `gt`, `gte`, `lt` и `lte` используют тот же порядок, что сортировка: без учёта регистра и с числовым сравнением фрагментов строки. Условие поля задаётся объектом с оператором, например `{ "id": { "eq": "1" } }`.
|
|
388
404
|
|
|
389
405
|
```json
|
|
390
406
|
{
|
|
@@ -417,7 +433,7 @@ npx deep-json-server examples/server.config.js
|
|
|
417
433
|
| PATCH | `/users/{id}` | `userUpdate` |
|
|
418
434
|
| DELETE | `/users/{id}` | `userDelete` |
|
|
419
435
|
|
|
420
|
-
Имя параметра пути соответствует первичному ключу. POST, PUT и PATCH принимают JSON-объект записи. PUT заменяет запись с сохранением ключа и серверных полей. PATCH объединяет поля на верхнем уровне; переданные вложенные объекты заменяются с сохранением их полей `readOnly`.
|
|
436
|
+
Имя параметра пути соответствует первичному ключу. POST, PUT и PATCH принимают JSON-объект записи. PUT заменяет запись с сохранением ключа и серверных полей. PATCH объединяет поля на верхнем уровне; переданные вложенные объекты заменяются с сохранением их полей `readOnly`. При создании и замене передавайте обязательные поля, доступные для записи и не имеющие значений по умолчанию. При обновлении проверяются переданные значения и итоговая запись. Отсутствующая запись — `404`, конфликт — `409`.
|
|
421
437
|
|
|
422
438
|
POST возвращает созданную запись со статусом `201`; PUT, PATCH и DELETE — результат со статусом `200`. Ошибки REST имеют вид `{ "error": "Error description" }`.
|
|
423
439
|
|
|
@@ -448,7 +464,11 @@ const params = new URLSearchParams({ scope: JSON.stringify(scope) });
|
|
|
448
464
|
const response = await fetch(`/users?${params}`);
|
|
449
465
|
```
|
|
450
466
|
|
|
451
|
-
Скаляры и массивы примитивов выбираются через `true`, объекты и связи — через свой массив `scope`. Если аргументы не нужны, в массиве остаётся только объект полей.
|
|
467
|
+
Скаляры и массивы примитивов выбираются через `true`, объекты и связи — через свой массив `scope`. Если аргументы не нужны, в массиве остаётся только объект полей.
|
|
468
|
+
|
|
469
|
+
`"*": true` включает только скалярные поля текущей модели, которые не хранят ключи связей. Массивы, объекты, связи, их ключи и поля `writeOnly` в него не входят.
|
|
470
|
+
|
|
471
|
+
Со схемой ключ связи можно выбрать через `{ "*": true, "countryId": true }`, только если он объявлен в `fields`. Без схемы ключ, присутствующий в записях базы, можно выбрать явно, даже если его нет в `*`; из `*` он исключается, когда сервер выводит связь по имени ключа и существующей коллекции.
|
|
452
472
|
|
|
453
473
|
Например, собственные поля фильма, пользователи актёров и отсортированные жанры:
|
|
454
474
|
|
|
@@ -472,9 +492,22 @@ const response = await fetch(`/users?${params}`);
|
|
|
472
492
|
|
|
473
493
|
Без `scope` возвращаются скалярные поля, как при `[{"*":true}]`. Пустой выбор `[{}]` возвращает объект без полей. Списки сохраняют структуру `{ data, total }`.
|
|
474
494
|
|
|
475
|
-
Аргументы доступны только у списков. Вместо них список может использовать `{ "union": [scope, ...] }`: каждая часть — обычный scope списка, части выполняются в порядке массива, а для каждого первичного ключа остаётся первая запись.
|
|
495
|
+
Аргументы доступны только у списков. Вместо них список может использовать `{ "union": [scope, ...] }`: каждая часть — обычный scope списка, части выполняются в порядке массива, а для каждого первичного ключа остаётся первая запись.
|
|
476
496
|
|
|
477
|
-
|
|
497
|
+
Во вложенных массивах объектов без первичных ключей каждый исходный элемент включается один раз, даже если его выбрали несколько частей. Разные элементы с одинаковым содержимым сохраняются отдельно.
|
|
498
|
+
|
|
499
|
+
В запросе отдельной записи и в ответе мутации аргументы можно задать для вложенных списков.
|
|
500
|
+
|
|
501
|
+
Параметры проверяются даже на пустых данных; ошибка в выборе ответа отменяет изменения записи. Некорректный `scope` возвращает `400`. Максимальная длина JSON — 10 000 символов, глубина выбора — 32 уровня.
|
|
502
|
+
|
|
503
|
+
REST и GraphQL ограничивают общий объём работы одного запроса:
|
|
504
|
+
|
|
505
|
+
- 1 000 элементов в дереве выбора полей.
|
|
506
|
+
- 10 000 записей, развёрнутых в ответе.
|
|
507
|
+
- 100 000 учитываемых шагов обработки, включая фильтрацию и сортировку.
|
|
508
|
+
- 8 МиБ для оценочного размера JSON-ответа.
|
|
509
|
+
|
|
510
|
+
Эти лимиты общие для вложенных списков, частей `union` и псевдонимов GraphQL. Размер оценивается с запасом на кодирование JSON, поэтому лимит может сработать, даже если фактический ответ меньше 8 МиБ. При превышении сервер возвращает REST `400` или GraphQL `INVALID_QUERY`; уменьшите число выбранных полей, вложенность или размер страниц. Если построение ответа REST превышает лимит, изменения записи откатываются.
|
|
478
511
|
|
|
479
512
|
### Вложенная запись
|
|
480
513
|
|
|
@@ -504,11 +537,15 @@ Content-Type: application/json
|
|
|
504
537
|
| Объект с первичным ключом и другими полями | PATCH обновляет переданные поля, PUT заменяет связанную запись |
|
|
505
538
|
| Объект без первичного ключа | Создать запись со значениями по умолчанию и сгенерированным ключом |
|
|
506
539
|
|
|
507
|
-
Имя и тип ключа берутся из целевой модели. Объект только с этим ключом считается ссылкой в POST, PUT и PATCH; остальные обязательные поля целевой модели ему не нужны.
|
|
540
|
+
Имя и тип ключа берутся из целевой модели. Объект только с этим ключом считается ссылкой в POST, PUT и PATCH; остальные обязательные поля целевой модели ему не нужны.
|
|
541
|
+
|
|
542
|
+
Если добавить другие поля, это уже обновление: PUT требует обязательные поля, доступные для записи и не имеющие значений по умолчанию, удаляет пропущенные необязательные или задаёт им значения по умолчанию; POST/PATCH обновляют переданные поля. При замене сохраняются первичный ключ, генерируемые значения и поля `readOnly`.
|
|
543
|
+
|
|
544
|
+
Если запись по ключу не найдена, операция завершится ошибкой. Для создания вложенной записи без ключа нужна его автоматическая генерация. Объявленные хранимые ключи, например `countryId`, тоже позволяют менять ссылки напрямую.
|
|
508
545
|
|
|
509
546
|
Переданный список заменяет состав связи. PATCH сохраняет пропущенные связи. PUT заменяет доступные для записи поля текущей записи, включая её собственные ключи связей, но сохраняет пропущенные связи `keyOn: "related"`, чьи ключи хранятся в других записях. `[]` очищает список, `null` — одиночную связь с разрешённым `nullable`. Разрыв связи не удаляет связанную запись. Обязательная одиночная связь должна сохраняться; обязательная связь-список может быть пустой.
|
|
510
547
|
|
|
511
|
-
Если хранимый ключ объявлен в `fields`, в одном объекте указывайте либо связь, либо её ключ: например, `genres` или `genreIds`. Одновременная передача обоих полей возвращает `400 INVALID_INPUT`, даже если наборы ключей совпадают:
|
|
548
|
+
Если хранимый ключ объявлен в `fields`, в одном объекте указывайте либо связь, либо её ключ: например, `genres` или `genreIds`. Одновременная передача обоих полей возвращает REST `400` или GraphQL `INVALID_INPUT`, даже если наборы ключей совпадают:
|
|
512
549
|
|
|
513
550
|
```json
|
|
514
551
|
{
|
|
@@ -517,9 +554,9 @@ Content-Type: application/json
|
|
|
517
554
|
}
|
|
518
555
|
```
|
|
519
556
|
|
|
520
|
-
Сервер
|
|
557
|
+
Сервер отклоняет запрос с обоими полями и отменяет всю операцию. Поле связи принимает только объекты; чтобы изменить ссылки без создания и обновления связанных записей, используйте объявленный ключ, например `genreIds`. Для обратной связи сервер меняет целевой ключ. Если путь проходит через массив и нельзя однозначно выбрать элемент для связи, передайте массив с нужными ключами явно. Защищённые ключи изменять нельзя.
|
|
521
558
|
|
|
522
|
-
Все вложенные изменения входят в транзакцию основной записи. Ошибка
|
|
559
|
+
Все вложенные изменения входят в транзакцию основной записи. Ошибка проверки или отсутствующая целевая запись отменяет все изменения. В REST неверный выбор полей ответа тоже приводит к откату. GraphQL проверяет выбор полей до записи, но ошибка при построении результата уже сохранённой мутации не отменяет её; подробнее в разделе [GraphQL](#graphql). Изменения общей записи видны всем, кто с ней связан.
|
|
523
560
|
|
|
524
561
|
GraphQL принимает в полях связей типизированные объекты. Чтобы изменить только ссылки, в том числе при замене, передайте объекты только с первичными ключами либо объявленные поля ключей, например `genreIds`. Обязательные поля вложенного создания или замены с дополнительными полями проверяются при выполнении. Пример:
|
|
525
562
|
|
|
@@ -541,7 +578,7 @@ mutation {
|
|
|
541
578
|
Укажите `database.schema`, включите `graphql` в корневой `api` схемы и добавьте в конфигурацию `graphql: {}`:
|
|
542
579
|
|
|
543
580
|
```sh
|
|
544
|
-
npx deep-json-server server.config.
|
|
581
|
+
npx deep-json-server server.config.mjs
|
|
545
582
|
```
|
|
546
583
|
|
|
547
584
|
Отправляйте запросы на `/graphql` методом POST с `Content-Type: application/json` и телом `{ "query": "…", "variables": {} }`. Путь можно изменить через `graphql.endpoint`.
|
|
@@ -577,13 +614,13 @@ query {
|
|
|
577
614
|
|
|
578
615
|
Строковый первичный ключ представлен типом GraphQL `ID`, обычные строки — `String`, числа — `Float`, параметры пагинации — `Int`. Enum сохраняет допустимые строковые имена; остальные значения получают имена `VALUE_0`, `VALUE_1` и т. д. Длины строк, форматы и другие ограничения модели проверяются сервером при выполнении запроса. Для просмотра схемы доступна интроспекция. Параметры выбранных списков проверяются до выполнения мутаций.
|
|
579
616
|
|
|
580
|
-
Ошибки содержат `extensions.code`: `INVALID_INPUT`, `INVALID_QUERY`, `NOT_FOUND`, `CONFLICT`, `UNAUTHENTICATED`, `FORBIDDEN` или `INTERNAL_ERROR`. Ошибки синтаксиса и типов GraphQL возвращаются в стандартном массиве `errors`. Максимальная глубина запроса — 32 уровня.
|
|
617
|
+
Ошибки содержат `extensions.code`: `INVALID_INPUT`, `INVALID_QUERY`, `NOT_FOUND`, `CONFLICT`, `UNAUTHENTICATED`, `FORBIDDEN` или `INTERNAL_ERROR`. Ошибки синтаксиса и типов GraphQL возвращаются в стандартном массиве `errors`. Максимальная глубина запроса — 32 уровня. Лимиты запросов, описанные для REST, действуют и в GraphQL. Число элементов выбора полей и аргументы списков проверяются до выполнения мутаций. Ошибки при построении результата, в том числе превышение лимита запроса, не отменяют уже сохранённую мутацию; перед повтором проверьте состояние записи.
|
|
581
618
|
|
|
582
|
-
Операция GraphQL с несколькими полями мутаций не является одной транзакцией: поля выполняются по порядку, а ранее сохранённые изменения остаются при ошибке последующего результата. Превышение
|
|
619
|
+
Операция GraphQL с несколькими полями мутаций не является одной транзакцией: поля выполняются по порядку, а ранее сохранённые изменения остаются при ошибке последующего результата. Превышение лимита во время выполнения возвращает `data: null` и останавливает следующие поля мутаций. Перед повтором проверьте сохранённое состояние запросом с меньшей выборкой: `data: null` не означает откат, а повторное создание записи с генерируемым ключом может привести к дубликату.
|
|
583
620
|
|
|
584
621
|
## OpenAPI и экспорт схем
|
|
585
622
|
|
|
586
|
-
|
|
623
|
+
Спецификация соответствует OpenAPI 3.0.3. Добавьте `openapi: {}`, `database.schema` и `package.source`, чтобы получать её по адресу `/openapi.json`. Путь меняется через `openapi.endpoint`. Маршруты базы включаются только для REST-моделей; если база доступна только через GraphQL, документ всё равно может описывать настроенные маршруты аутентификации и файлов. Поля OpenAPI `info.title`, `info.version` и необязательное `info.description` берутся из настроенного `package.json`. Спецификацию можно открыть в Swagger UI или импортировать в API-клиент.
|
|
587
624
|
|
|
588
625
|
Для сохранения схем задайте пути экспорта:
|
|
589
626
|
|
|
@@ -598,13 +635,13 @@ export default {
|
|
|
598
635
|
```
|
|
599
636
|
|
|
600
637
|
```bash
|
|
601
|
-
npx deep-json-server server.config.
|
|
602
|
-
npx deep-json-server server.config.
|
|
638
|
+
npx deep-json-server server.config.mjs --generate-only
|
|
639
|
+
npx deep-json-server server.config.mjs --generate
|
|
603
640
|
```
|
|
604
641
|
|
|
605
642
|
`--generate-only` экспортирует и завершает работу, `--generate` после экспорта запускает сервер. Форматы определяются наличием секций. Для каждого выбранного формата обязателен свой `target`. HTTP-маршрут OpenAPI возвращает JSON; `writeOpenapi()` и CLI записывают YAML. Если секций нет, отсутствует `target` или генерация завершилась ошибкой, команда возвращает ошибку и сервер не запускается.
|
|
606
643
|
|
|
607
|
-
|
|
644
|
+
Генерация не читает записи базы, учётные записи и загруженные файлы. Перед записью проверяются все выбранные `target`: они не могут перезаписать конфигурацию, базу, схему, пользователей, блокировки, счётчики или метаданные файлов.
|
|
608
645
|
|
|
609
646
|
## Аутентификация
|
|
610
647
|
|
|
@@ -639,7 +676,7 @@ export default {
|
|
|
639
676
|
};
|
|
640
677
|
```
|
|
641
678
|
|
|
642
|
-
Запустите `npx deep-json-server server.config.
|
|
679
|
+
Запустите `npx deep-json-server server.config.mjs`. Каждой исходной учётной записи нужны уникальный строковый `id`, уникальный `username` и `passwordHash`, созданный функцией выше. `isAdmin` по умолчанию равен `false`, а `sessions` — `[]`, поэтому существующие файлы пользователей остаются допустимыми. Пароли хешируются через scrypt со случайной солью.
|
|
643
680
|
|
|
644
681
|
При `storage: 'memory'` передайте массив в `auth.source`:
|
|
645
682
|
|
|
@@ -660,13 +697,13 @@ export default {
|
|
|
660
697
|
};
|
|
661
698
|
```
|
|
662
699
|
|
|
663
|
-
Учётные записи
|
|
700
|
+
Учётные записи и сессии хранятся вместе в `auth.source`, отдельно от базы. Каждый элемент массива `sessions` содержит `{ tokenHash, expiresAt }`. `tokenHash` — хеш токена SHA-256: строка из 64 шестнадцатеричных символов в нижнем регистре. `expiresAt` — время окончания сессии, выраженное числом миллисекунд с 1 января 1970 года, 00:00 UTC. Сами токены не сохраняются, а ответы методов аутентификации не раскрывают хеши паролей и данные сессий. Хеши токенов должны быть уникальны среди всех пользователей.
|
|
664
701
|
|
|
665
|
-
При `storage: 'file'`
|
|
702
|
+
При `storage: 'file'` изменения учётных записей и сессий сохраняются в JSON-файл до отправки успешного ответа. Действующие сессии сохраняются после перезапуска сервера: клиент может продолжать использовать прежний токен. Новый пароль и удаление всех сессий пользователя записываются вместе. При ошибке записи пользователи и сессии остаются без изменений. Для ручного редактирования файла остановите сервер; если меняете хеш пароля, также очистите `sessions` этого пользователя, затем запустите сервер снова.
|
|
666
703
|
|
|
667
|
-
При `storage: 'memory'` переданный массив пользователей не изменяется. Каждый запуск заново загружает исходных пользователей и сессии. Сессии, созданные во время работы, после перезапуска
|
|
704
|
+
При `storage: 'memory'` переданный массив пользователей не изменяется. Каждый запуск заново загружает исходных пользователей и сессии. Сессии, созданные во время работы, после перезапуска исчезают. Действующие сессии из исходных данных восстанавливаются, даже если при предыдущем запуске пользователь вышел через `/auth/logout`. Клиенту по-прежнему нужен сам токен: восстановить его из `tokenHash` нельзя.
|
|
668
705
|
|
|
669
|
-
`auth.expiresIn` задаёт срок новых сессий и по умолчанию равен 3600 секундам. У существующих сессий сохраняется записанное время истечения, в том числе после перезапуска. Истёкшие сессии игнорируются при загрузке, отклоняются при каждом запросе и удаляются у всех пользователей при следующем успешном изменении
|
|
706
|
+
`auth.expiresIn` задаёт срок новых сессий и по умолчанию равен 3600 секундам. У существующих сессий сохраняется записанное время истечения, в том числе после перезапуска. Истёкшие сессии игнорируются при загрузке, отклоняются при каждом запросе и удаляются у всех пользователей при следующем успешном изменении данных пользователей или сессий, например при входе или выходе. Срок сессии автоматически не продлевается.
|
|
670
707
|
|
|
671
708
|
| REST-запрос | JSON тела | Ответ |
|
|
672
709
|
|---|---|---|
|
|
@@ -677,7 +714,7 @@ export default {
|
|
|
677
714
|
| `PATCH /auth/users/:id/password` | `{ "currentPassword": "…", "newPassword": "…" }` | `{ success: true }` |
|
|
678
715
|
| `PATCH /auth/users/:id/admin` | `{ "isAdmin": true }` | `{ id, username, isAdmin }` |
|
|
679
716
|
|
|
680
|
-
Регистрация и вход доступны без токена. Для остальных методов передавайте `Authorization: Bearer <accessToken>`. Регистрация создаёт обычного пользователя с новым `id`; поля `id`, `passwordHash`, `isAdmin` и `sessions` в запросе запрещены.
|
|
717
|
+
Регистрация и вход доступны без токена. Для остальных методов передавайте `Authorization: Bearer <accessToken>`. Регистрация создаёт обычного пользователя с новым `id`; поля `id`, `passwordHash`, `isAdmin` и `sessions` в запросе запрещены. Имена пользователей уникальны с учётом регистра; повторная регистрация того же имени возвращает `409`. Имя должно содержать хотя бы один непробельный символ и не более 256 символов, пароль — от 1 до 1024 символов. Пробелы по краям не удаляются. Регистрация не создаёт сессию: после неё выполните вход.
|
|
681
718
|
|
|
682
719
|
Пользователь меняет только свой пароль, передавая `currentPassword` и `newPassword`. Для администратора при смене своего пароля действует то же правило. Администратор может сменить пароль обычному пользователю, передав только `newPassword`. Изменение пароля другого администратора возвращает `403`. После успешной смены завершаются все сессии этого пользователя, включая текущую при смене своего пароля; требуется новый вход. Неверный текущий пароль возвращает `401` без изменения сессий.
|
|
683
720
|
|
|
@@ -685,7 +722,7 @@ export default {
|
|
|
685
722
|
|
|
686
723
|
Недействительный или истёкший токен возвращает `401`, недостаток прав — `403`, отсутствующий пользователь при разрешённой операции — `404`. Ошибки тела запроса возвращают `400`. Вход, регистрация и смена пароля могут вернуть `429` при превышении числа одновременных вычислений паролей; вход также возвращает `429`, если уже есть 10 000 активных сессий среди всех пользователей, включая загруженные из источника. Выход завершает только сессию переданного токена.
|
|
687
724
|
|
|
688
|
-
При
|
|
725
|
+
При включённой аутентификации у каждой записи модели доступен виртуальный объект `actions`. Он вычисляется для текущего пользователя и не сохраняется в базе. В REST его нужно явно запросить через `scope`:
|
|
689
726
|
|
|
690
727
|
```json
|
|
691
728
|
[
|
|
@@ -715,9 +752,9 @@ query {
|
|
|
715
752
|
}
|
|
716
753
|
```
|
|
717
754
|
|
|
718
|
-
`actions` доступен у корневых и связанных записей моделей, включая результаты мутаций. Его нельзя записывать, фильтровать или сортировать. Обычные вложенные объекты и ответы методов
|
|
755
|
+
`actions` доступен у корневых и связанных записей моделей, включая результаты мутаций. Его нельзя записывать, фильтровать или сортировать. Обычные вложенные объекты и ответы методов аутентификации это поле не получают.
|
|
719
756
|
|
|
720
|
-
Когда настроен `openapi`, его документ описывает маршруты
|
|
757
|
+
Когда настроен `openapi`, его документ описывает маршруты аутентификации, необязательную Bearer-аутентификацию при чтении записей, обязательную при изменениях и поле ответа `actions`. В Swagger UI токен из ответа на вход можно вставить в **Authorize**. Для экспорта схем включите аутентификацию в конфигурации и выполните `npx deep-json-server server.config.mjs --generate-only`; файл учётных записей при генерации не читается. Методы аутентификации доступны через REST. В GraphQL тот же токен проверяется при чтении прав и изменении записей. Для GraphQL и OpenAPI нужна `database.schema`.
|
|
721
758
|
|
|
722
759
|
## Даты записей, удаление и владельцы
|
|
723
760
|
|
|
@@ -756,9 +793,9 @@ query {
|
|
|
756
793
|
| `createdById`, `updatedById` | `auth` | Создатель/владелец и последний редактор |
|
|
757
794
|
| `deletedById` | `auth` + `softDelete` | Пользователь, удаливший запись, или `null` |
|
|
758
795
|
|
|
759
|
-
Даты
|
|
796
|
+
Даты записываются строками ISO 8601 в UTC. У новой записи даты создания и изменения совпадают; при включённой аутентификации создателем и последним редактором становится текущий пользователь. PUT/PATCH сохраняют создателя и дату создания. DELETE обновляет поля удаления, а также дату и автора последнего изменения, если соответствующие функции включены. Для старых записей с неизвестными датами или авторами возвращается `null`. Эти поля доступны только для чтения в REST и GraphQL; обычные вложенные объекты собственных полей дат и авторства не получают. Отключение функции не удаляет её ранее сохранённые значения.
|
|
760
797
|
|
|
761
|
-
При мягком удалении DELETE оставляет запись в базе. Повторный DELETE уже удалённой записи сохраняет прежние сведения об удалении. Получение по первичному ключу возвращает и удалённые записи. Успешный PUT/PATCH восстанавливает запись, обнуляя `deletedAt` и `deletedById`. Пустой PATCH восстанавливает без замены остальных полей;
|
|
798
|
+
При мягком удалении DELETE оставляет запись в базе. Повторный DELETE уже удалённой записи сохраняет прежние сведения об удалении. Получение по первичному ключу возвращает и удалённые записи. Успешный PUT/PATCH восстанавливает запись, обнуляя `deletedAt` и `deletedById`. Пустой PATCH восстанавливает без замены остальных полей; PUT следует обычным правилам замены.
|
|
762
799
|
|
|
763
800
|
Списки по умолчанию возвращают активные записи. Для получения удалённых задайте условие по `deletedAt`:
|
|
764
801
|
|
|
@@ -773,9 +810,13 @@ query {
|
|
|
773
810
|
|
|
774
811
|
Каскадное удаление учитывает `onDelete` и настройку `softDelete` каждой затронутой модели. Восстановление записи, начавшей каскад, возвращает записи, удалённые той же операцией, не затрагивая удалённые ранее. Физически удалённые записи восстановить нельзя. Если каскад удалил вложенный объект, восстановление возможно, пока соответствующее поле объекта не менялось после удаления. Конфликт или отсутствие обязательной связи отменяет всю операцию.
|
|
775
812
|
|
|
776
|
-
Служебные
|
|
813
|
+
Служебные данные для восстановления хранятся вместе с записями и сохраняются после перезапуска; включайте их в резервную копию базы. Внутреннее поле `djsDeletion` зарезервировано и не выдаётся через API.
|
|
814
|
+
|
|
815
|
+
При включённой аутентификации создавать записи может любой вошедший пользователь. Изменять, удалять и восстанавливать — владелец по `createdById` или пользователь с `isAdmin: true`. Записи без владельца изменяет только администратор. Его правки не меняют владельца. Правила действуют на вложенные записи, изменение ключей связей, каскады и восстановление.
|
|
816
|
+
|
|
817
|
+
Ссылка через объект только с первичным ключом или объявленный хранимый ключ не требует владения неизменяемой целевой записью. Вложенный объект с дополнительными полями требует права изменять её. При изменении обратных связей нужны права на каждую запись, чей хранимый ключ меняется.
|
|
777
818
|
|
|
778
|
-
|
|
819
|
+
Изменение записи вместе с вложенными изменениями атомарно: отказ в правах сохраняет прежнее состояние всех записей, затронутых этим изменением. В GraphQL это относится к отдельному полю мутации; выполнение операций с несколькими полями описано в разделе [GraphQL](#graphql).
|
|
779
820
|
|
|
780
821
|
REST возвращает 401 при отсутствии действительного токена и 403 при недостатке прав. GraphQL проверяет мутации по тем же правилам и возвращает коды `UNAUTHENTICATED` или `FORBIDDEN`; токен получают через REST-вход и передают в `Authorization: Bearer <token>`. GraphQL-запросы чтения, OPTIONS и все операции с файлами остаются открытыми.
|
|
781
822
|
|
|
@@ -794,7 +835,7 @@ export default {
|
|
|
794
835
|
Запустите сервер:
|
|
795
836
|
|
|
796
837
|
```bash
|
|
797
|
-
npx deep-json-server server.config.
|
|
838
|
+
npx deep-json-server server.config.mjs
|
|
798
839
|
```
|
|
799
840
|
|
|
800
841
|
Для временных тестов выберите `storage: 'memory'` и передайте массив в `files.source`. Каждая начальная запись содержит `name`, `mimeType`, бинарное `content` в виде `Uint8Array` и необязательный `directory`. Загруженные файлы в таком режиме остаются в памяти до завершения процесса.
|
|
@@ -829,13 +870,13 @@ Content-Type: image/jpeg
|
|
|
829
870
|
Сочетание `directory` и `name` идентифицирует файл. Повторная загрузка по существующему пути возвращает `409`. Чтобы заменить файл, передайте `Content-Override: true`; успешная перезапись возвращает `200`. Сервер поддерживает следующие файловые маршруты:
|
|
830
871
|
|
|
831
872
|
```text
|
|
832
|
-
POST /_files/storage
|
|
833
|
-
GET /_files/storage/*
|
|
834
|
-
PATCH /_files/storage/*
|
|
835
|
-
DELETE /_files/storage/*
|
|
873
|
+
POST /_files/storage Загрузить или заменить файл
|
|
874
|
+
GET /_files/storage/* Получить содержимое файла
|
|
875
|
+
PATCH /_files/storage/* Переименовать или переместить файл
|
|
876
|
+
DELETE /_files/storage/* Удалить файл
|
|
836
877
|
|
|
837
|
-
GET /_files/metadata/*
|
|
838
|
-
GET /_files/download/*
|
|
878
|
+
GET /_files/metadata/* Получить метаданные в JSON
|
|
879
|
+
GET /_files/download/* Скачать файл как вложение
|
|
839
880
|
```
|
|
840
881
|
|
|
841
882
|
Для переименования, перемещения либо обеих операций отправьте JSON-объект. Нужно указать хотя бы одно поле:
|
|
@@ -854,7 +895,7 @@ Content-Type: application/json
|
|
|
854
895
|
|
|
855
896
|
При хранении на диске бинарный файл находится по пути `<files.source>/<directory>/<name>`. Метаданные по умолчанию хранятся в `<files.source>/.files.json`; другой путь можно задать через `files.metadata`. Директории и файл метаданных создаются по мере необходимости.
|
|
856
897
|
|
|
857
|
-
|
|
898
|
+
Пути внутри каталога файлового хранилища не могут содержать символические ссылки. Загрузка и переименование не могут перезаписать базу, её блокировку, счётчики, схему, учётные записи, загруженную конфигурацию или файл метаданных. Для ручного изменения файлов или метаданных остановите сервер. Общие дисковые источники защищены блокировками, описанными в разделе [хранение данных](#хранение-данных).
|
|
858
899
|
|
|
859
900
|
Отправляйте файл как бинарное тело запроса. В браузере для этого можно использовать `xhr.send(file)`, а прогресс отслеживать через `XMLHttpRequest.upload.onprogress`. Максимальный размер по умолчанию равен 100 МиБ и настраивается через `server.maxFileSize`. Отсутствующие или небезопасные заголовки и пути возвращают `400`, превышение лимита — `413`, а отсутствующий, некорректный или не поддерживаемый Fastify `Content-Type` — `400` либо `415` в зависимости от этапа проверки.
|
|
860
901
|
|
|
@@ -862,7 +903,7 @@ Content-Type: application/json
|
|
|
862
903
|
|
|
863
904
|
```js
|
|
864
905
|
import { createServer } from '@kollors/deep-json-server/server';
|
|
865
|
-
import config from './server.config.
|
|
906
|
+
import config from './server.config.mjs';
|
|
866
907
|
|
|
867
908
|
const facade = await createServer(config);
|
|
868
909
|
const server = facade.fastify();
|
|
@@ -870,9 +911,9 @@ await server.listen();
|
|
|
870
911
|
// await server.close();
|
|
871
912
|
```
|
|
872
913
|
|
|
873
|
-
|
|
914
|
+
`fastify()` возвращает экземпляр сервера для настройки и запуска. База и включённые сервисы инициализируются при `ready()`, `listen()` или первом `inject()`; ошибка инициализации останавливает запуск.
|
|
874
915
|
|
|
875
|
-
`createServer(config)` принимает тот же объект конфигурации, что и CLI.
|
|
916
|
+
`createServer(config)` принимает тот же объект конфигурации, что и CLI. Для методов `openapi()` и `graphql()` нужны `database.schema` и соответствующая секция конфигурации. OpenAPI также требует `package.source`. Оба метода возвращают документы схем и не записывают файлы.
|
|
876
917
|
|
|
877
918
|
Полученный экземпляр Fastify принимает обычные параметры `listen()`, включая путь Unix-сокета. Вызов `listen()` без параметров использует `server.host` и `server.port`.
|
|
878
919
|
|
|
@@ -888,13 +929,17 @@ await writeOpenapi(document, './generated/openapi.yaml');
|
|
|
888
929
|
await writeGraphql(sdl, './generated/schema.graphql');
|
|
889
930
|
```
|
|
890
931
|
|
|
891
|
-
Отдельные генераторы принимают путь или объект схемы и не требуют конфигурации сервера. Если корневой `api` не задан, `generateOpenapi()` использует REST по умолчанию, а `generateGraphql()` — GraphQL. Явные значения `api` в корне и у моделей сохраняют силу; для выбранного формата нужна хотя бы одна доступная модель. `timestamps` и `softDelete` берутся из схемы. Опция `{ auth: true }` добавляет поля владельца и `actions`; OpenAPI также описывает маршруты
|
|
932
|
+
Отдельные генераторы принимают путь или объект схемы и не требуют конфигурации сервера. Если корневой `api` не задан, `generateOpenapi()` использует REST по умолчанию, а `generateGraphql()` — GraphQL. Явные значения `api` в корне и у моделей сохраняют силу; для выбранного формата нужна хотя бы одна доступная модель. `timestamps` и `softDelete` берутся из схемы. Опция `{ auth: true }` добавляет поля владельца и `actions`; OpenAPI также описывает маршруты аутентификации и требования токена. `hashPassword()` доступна и через общий импорт пакета.
|
|
892
933
|
|
|
893
|
-
Для `generateOpenapi()` обязателен `packagePath`; дополнительно доступны `host`, `port`, `pageSize` и `maxPageSize`. Метаданные OpenAPI читаются из указанного пакета.
|
|
934
|
+
Для `generateOpenapi()` обязателен `packagePath`; дополнительно доступны `host`, `port`, `pageSize` и `maxPageSize`. Метаданные OpenAPI читаются из указанного пакета. Сервер и генераторы работают с собственной копией модели. Размеры страниц должны быть положительными целыми числами; `pageSize` не может превышать `maxPageSize`.
|
|
894
935
|
|
|
895
936
|
## Хранение данных
|
|
896
937
|
|
|
897
|
-
Изменения выполняются последовательно внутри экземпляра сервера и проверяются на копии данных до сохранения.
|
|
938
|
+
Изменения выполняются последовательно внутри экземпляра сервера и проверяются на копии данных до сохранения. Счётчики `increment` хранятся рядом с базой в `<database path>.counters.json`; сохраняйте этот файл вместе с базой. Номера резервируются до записи данных: сбой может оставить пропуск, но не приводит к повторной выдаче номера.
|
|
939
|
+
|
|
940
|
+
Для каждого записываемого дискового источника используйте один процесс сервера. Второй сервер с общей базой, файлом пользователей, каталогом файлового хранилища или метаданными не запустится. Для блокировки рядом с источником создаётся JSON-файл: например, для `database.json` — `database-lock.json`. Последнее расширение имени источника заменяется на `-lock.json`. Файл содержит тип блокировки, PID процесса и токен владельца.
|
|
941
|
+
|
|
942
|
+
При закрытии сервера файлы блокировок удаляются. После аварии ОС освобождает блокировки, а следующий сервер использует оставшиеся файлы. Дисковые блокировки работают через нативную зависимость `fs-native-extensions`; хранение в памяти и генерация схем её не загружают. Если источник задан символической ссылкой, сервер блокирует и путь ссылки, и целевой путь. Перед ручным изменением любого хранилища остановите сервер.
|
|
898
943
|
|
|
899
944
|
## Разработка
|
|
900
945
|
|
|
@@ -905,8 +950,10 @@ npm ci
|
|
|
905
950
|
npm run verify
|
|
906
951
|
```
|
|
907
952
|
|
|
908
|
-
`npm run verify` проверяет
|
|
953
|
+
`npm run verify` сначала проверяет рабочие зависимости через `npm audit`, затем запускает проверку типов, линтер, тесты с порогами покрытия и проверку установки пакета из архива. Для установленного пакета проверяются публичные типы, запуск CLI с HTTP-запросами и восстановление после аварийного завершения процесса.
|
|
954
|
+
|
|
955
|
+
Для предварительной версии обновите номер в `package.json` и `package-lock.json`, затем отправьте коммит в `main`. Для стабильного релиза дополнительно отправьте тег `v<version>`, например `v1.0.0`. Сервер и CLI читают номер версии из метаданных пакета.
|
|
909
956
|
|
|
910
|
-
|
|
957
|
+
GitHub Actions выполняет полную проверку на Linux, Windows и macOS с Node.js 22, 24 и 26. Публикация начинается только после успеха всех девяти запусков. Для предварительных версий тег создаётся автоматически, а пакет публикуется в канал `alpha`, `beta` или `rc`; стабильные версии попадают в `latest`. npm проверяет подлинность запуска через trusted publishing, поэтому хранить npm-токен в секретах не требуется.
|
|
911
958
|
|
|
912
959
|
Лицензия: MIT.
|