@kollors/deep-json-server 1.0.0-alpha.7 → 1.0.0-alpha.9

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 (127) hide show
  1. package/README.md +168 -109
  2. package/README.ru.md +171 -112
  3. package/dist/index.d.ts +5 -6
  4. package/dist/index.js +2 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/auth/contract.d.ts +6 -2
  7. package/dist/src/auth/contract.js +8 -1
  8. package/dist/src/auth/contract.js.map +1 -1
  9. package/dist/src/auth/input.d.ts +18 -0
  10. package/dist/src/auth/input.js +34 -0
  11. package/dist/src/auth/input.js.map +1 -0
  12. package/dist/src/auth/password.d.ts +4 -0
  13. package/dist/src/auth/password.js +5 -1
  14. package/dist/src/auth/password.js.map +1 -1
  15. package/dist/src/auth/routes.d.ts +1 -1
  16. package/dist/src/auth/routes.js +4 -1
  17. package/dist/src/auth/routes.js.map +1 -1
  18. package/dist/src/auth/service.d.ts +48 -9
  19. package/dist/src/auth/service.js +143 -68
  20. package/dist/src/auth/service.js.map +1 -1
  21. package/dist/src/auth/store.d.ts +26 -0
  22. package/dist/src/auth/store.js +82 -0
  23. package/dist/src/auth/store.js.map +1 -0
  24. package/dist/src/cli/index.d.ts +2 -2
  25. package/dist/src/cli/index.js +70 -84
  26. package/dist/src/cli/index.js.map +1 -1
  27. package/dist/src/core/config-values.d.ts +28 -0
  28. package/dist/src/core/config-values.js +53 -0
  29. package/dist/src/core/config-values.js.map +1 -0
  30. package/dist/src/core/constants.d.ts +1 -1
  31. package/dist/src/core/constants.js +1 -1
  32. package/dist/src/core/database.d.ts +10 -12
  33. package/dist/src/core/database.js +4 -4
  34. package/dist/src/core/database.js.map +1 -1
  35. package/dist/src/core/engine.d.ts +3 -2
  36. package/dist/src/core/engine.js +53 -44
  37. package/dist/src/core/engine.js.map +1 -1
  38. package/dist/src/core/lifecycle/mutation.d.ts +3 -3
  39. package/dist/src/core/lifecycle/mutation.js +2 -2
  40. package/dist/src/core/lifecycle/mutation.js.map +1 -1
  41. package/dist/src/core/lifecycle/options.d.ts +1 -0
  42. package/dist/src/core/lifecycle/options.js +2 -4
  43. package/dist/src/core/lifecycle/options.js.map +1 -1
  44. package/dist/src/core/model.d.ts +18 -3
  45. package/dist/src/core/model.js +17 -7
  46. package/dist/src/core/model.js.map +1 -1
  47. package/dist/src/core/mutations/write.d.ts +1 -2
  48. package/dist/src/core/mutations/write.js.map +1 -1
  49. package/dist/src/core/operations.d.ts +33 -0
  50. package/dist/src/core/operations.js +10 -0
  51. package/dist/src/core/operations.js.map +1 -0
  52. package/dist/src/core/pagination.d.ts +2 -2
  53. package/dist/src/core/pagination.js +3 -5
  54. package/dist/src/core/pagination.js.map +1 -1
  55. package/dist/src/core/paths.d.ts +2 -2
  56. package/dist/src/core/paths.js +15 -5
  57. package/dist/src/core/paths.js.map +1 -1
  58. package/dist/src/core/storage.d.ts +2 -0
  59. package/dist/src/core/storage.js +2 -0
  60. package/dist/src/core/storage.js.map +1 -0
  61. package/dist/src/files/contract.d.ts +6 -7
  62. package/dist/src/files/contract.js.map +1 -1
  63. package/dist/src/files/disk-store.js +3 -20
  64. package/dist/src/files/disk-store.js.map +1 -1
  65. package/dist/src/files/index.d.ts +1 -1
  66. package/dist/src/files/index.js +5 -2
  67. package/dist/src/files/index.js.map +1 -1
  68. package/dist/src/files/memory-store.js +16 -16
  69. package/dist/src/files/memory-store.js.map +1 -1
  70. package/dist/src/files/streams.d.ts +5 -0
  71. package/dist/src/files/streams.js +20 -0
  72. package/dist/src/files/streams.js.map +1 -0
  73. package/dist/src/graphql/entry.d.ts +11 -2
  74. package/dist/src/graphql/entry.js +12 -2
  75. package/dist/src/graphql/entry.js.map +1 -1
  76. package/dist/src/graphql/{public.d.ts → generate.d.ts} +3 -2
  77. package/dist/src/graphql/{public.js → generate.js} +4 -2
  78. package/dist/src/graphql/generate.js.map +1 -0
  79. package/dist/src/graphql/resolvers.js +1 -1
  80. package/dist/src/graphql/resolvers.js.map +1 -1
  81. package/dist/src/graphql/schema.js +6 -3
  82. package/dist/src/graphql/schema.js.map +1 -1
  83. package/dist/src/openapi/auth.d.ts +2 -2
  84. package/dist/src/openapi/auth.js +82 -15
  85. package/dist/src/openapi/auth.js.map +1 -1
  86. package/dist/src/openapi/document.js +16 -13
  87. package/dist/src/openapi/document.js.map +1 -1
  88. package/dist/src/openapi/entry.d.ts +12 -1
  89. package/dist/src/openapi/entry.js +12 -1
  90. package/dist/src/openapi/entry.js.map +1 -1
  91. package/dist/src/openapi/{public.d.ts → generate.d.ts} +9 -16
  92. package/dist/src/openapi/generate.js +41 -0
  93. package/dist/src/openapi/generate.js.map +1 -0
  94. package/dist/src/openapi/options.d.ts +18 -0
  95. package/dist/src/openapi/options.js +13 -0
  96. package/dist/src/openapi/options.js.map +1 -0
  97. package/dist/src/openapi/write.d.ts +5 -0
  98. package/dist/src/openapi/write.js +12 -0
  99. package/dist/src/openapi/write.js.map +1 -0
  100. package/dist/src/rest/routes.js +6 -9
  101. package/dist/src/rest/routes.js.map +1 -1
  102. package/dist/src/server/config.d.ts +29 -34
  103. package/dist/src/server/config.js +83 -211
  104. package/dist/src/server/config.js.map +1 -1
  105. package/dist/src/server/create.d.ts +3 -5
  106. package/dist/src/server/create.js +35 -29
  107. package/dist/src/server/create.js.map +1 -1
  108. package/dist/src/server/features.d.ts +0 -11
  109. package/dist/src/server/features.js +0 -20
  110. package/dist/src/server/features.js.map +1 -1
  111. package/dist/src/server/model.d.ts +6 -0
  112. package/dist/src/server/model.js +14 -0
  113. package/dist/src/server/model.js.map +1 -0
  114. package/dist/src/server/public.d.ts +1 -2
  115. package/package.json +1 -1
  116. package/dist/src/graphql/lazy.d.ts +0 -10
  117. package/dist/src/graphql/lazy.js +0 -13
  118. package/dist/src/graphql/lazy.js.map +0 -1
  119. package/dist/src/graphql/public.js.map +0 -1
  120. package/dist/src/openapi/index.d.ts +0 -13
  121. package/dist/src/openapi/index.js +0 -37
  122. package/dist/src/openapi/index.js.map +0 -1
  123. package/dist/src/openapi/lazy.d.ts +0 -11
  124. package/dist/src/openapi/lazy.js +0 -13
  125. package/dist/src/openapi/lazy.js.map +0 -1
  126. package/dist/src/openapi/public.js +0 -26
  127. package/dist/src/openapi/public.js.map +0 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A JSON mock server with REST, GraphQL, related records, file uploads and schema exports. Supports user login, owner and administrator permissions, record timestamps and soft deletion. Requires Node.js 22 or newer.
6
6
 
7
- **1.0.0-alpha.7 is a prerelease.** REST queries use `scope=[fields, arguments?]` at every level. When upgrading from an earlier version, update query parameters using the examples below; upgrading from 0.x also requires the new model schema.
7
+ **1.0.0-alpha.9 is a prerelease.** When upgrading from the previous alpha, add `storage`, use `source` for data sources, move definitions under `models`, and move timestamps and soft deletion settings to the schema root. Configuration sections now enable modules; use `--generate` or `--generate-only` for exports. The second `createServer` argument has been removed.
8
8
 
9
9
  ## Installation
10
10
 
@@ -12,7 +12,7 @@ A JSON mock server with REST, GraphQL, related records, file uploads and schema
12
12
  npm install @kollors/deep-json-server@alpha
13
13
  ```
14
14
 
15
- To install a specific version, use `@1.0.0-alpha.7`.
15
+ To install a specific version, use `@1.0.0-alpha.9`.
16
16
 
17
17
  ## Quick start
18
18
 
@@ -31,9 +31,7 @@ Create two files in the same directory.
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 @@ Add a [model schema](#model-schema) to define relations and validation. See [que
46
44
 
47
45
  ## Configuration
48
46
 
47
+ With `storage: 'file'`, all sources and the schema are paths. With `'memory'`, they are in-memory data. Modes cannot be mixed. The presence of `auth`, `files`, `graphql` and `openapi` enables those modules; `graphql: {}` and `openapi: {}` use the default endpoints.
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: { path: './generated/schema.graphql' },
56
+ openapi: { path: './generated/openapi.yaml' },
57
+ server: { host: '127.0.0.1', port: 4001 },
58
+ };
59
+ ```
60
+
49
61
  | Setting | Meaning |
50
62
  |---|---|
51
- | `database.path` / `database.data` | Choose one to start the server: a JSON file or an in-memory collection object |
52
- | `database.schema` | Model object or JSON schema-file path; optional for REST |
53
- | `database.timestamps` | Add creation and update times; default `false` |
54
- | `database.softDelete` | Mark records as deleted and allow restoration; default `false` |
55
- | `openapi.enabled` | Enable the specification endpoint; default `false` |
56
- | `openapi.endpoint` | Specification path; default `/openapi.json` |
57
- | `openapi.path` | YAML export destination |
58
- | `openapi.info` | Optional metadata object: required `title` and `version`, optional `description` |
59
- | `graphql.enabled` | Enable GraphQL HTTP endpoint; default `false` |
60
- | `graphql.endpoint` | Endpoint path; default `/graphql` |
61
- | `graphql.path` | GraphQL SDL export destination |
62
- | `auth.users` | Path to a JSON array of auth users, or an in-memory array |
63
+ | `storage` | Required: `file` or `memory`; applies to all sources and the schema |
64
+ | `database.source` | Database JSON path or collection object |
65
+ | `database.schema` | Schema JSON path or schema object; optional for REST |
66
+ | `auth.source` | Users JSON path or user array |
63
67
  | `auth.expiresIn` | Session lifetime in seconds; default 3600 |
68
+ | `files.source` | Files directory or initial file array |
69
+ | `files.metadata` | For `file` only: metadata JSON path; defaults to `_database.json` inside `files.source` |
70
+ | `graphql.endpoint` | HTTP endpoint; default `/graphql` |
71
+ | `graphql.path` | GraphQL SDL export destination |
72
+ | `openapi.endpoint` | HTTP endpoint; default `/openapi.json` |
73
+ | `openapi.path` | OpenAPI export destination |
74
+ | `openapi.info` | Metadata: required `title` and `version`, optional `description` |
64
75
  | `server.host`, `server.port` | Defaults `127.0.0.1`, `4001`; CLI also reads `HOST`/`PORT` |
65
76
  | `server.pageSize`, `server.maxPageSize` | Defaults 10 and 100; default size is capped by the maximum |
66
77
  | `server.cors`, `server.logger` | Default `true`; logger also accepts Fastify logger options |
67
78
  | `server.maxFileSize` | Default 100 MiB |
68
- | `files.data` | In-memory binary files |
69
- | `files.directory`, `files.metadata` | Disk storage directory and metadata JSON file; both required |
70
79
 
71
- Relative paths resolve from the configuration file's directory. When passing a configuration object to `createServer()`, paths resolve from the working directory. The server works with a copy of in-memory input and metadata.
72
-
73
- Set `server.port` to `0` to let the operating system choose an available port. The OpenAPI endpoint uses a relative server URL.
80
+ Relative paths resolve from the configuration file directory, or from the working directory with `createServer(config)`. In-memory data, including the schema, is copied. Port `0` lets the system choose an available port.
74
81
 
75
82
  ### CLI
76
83
 
77
- | CLI flag | Action |
84
+ | Flag | Action |
78
85
  |---|---|
79
- | `--files` | Enable file routes; requires `files` configuration |
80
- | `--timestamps` | Enable record timestamps globally |
81
- | `--soft-delete` | Enable soft deletion globally |
82
- | `--graphql` | Enable the GraphQL API |
83
- | `--openapi` | Enable the OpenAPI endpoint |
86
+ | `--generate` | Export schemas, then start the server |
87
+ | `--generate-only` | Export schemas and exit |
84
88
  | `--host <host>` | Server address |
85
89
  | `--port <port>` | Server port |
86
- | `--help`, `-h` | Show help |
87
- | `--version`, `-v` | Show package version |
90
+ | `--help, -h` | Help |
91
+ | `--version, -v` | Package version |
88
92
 
89
- Host and port priority: CLI → configuration → `HOST`/`PORT` → defaults. The `files` section enables file routes; `auth` enables login and permission checks. Model settings can override global `timestamps` and `softDelete` values.
93
+ Address precedence: CLI → configuration → `HOST`/`PORT` → defaults. Without generation flags, only the server starts. `--generate` and `--generate-only` are mutually exclusive.
90
94
 
91
95
  ## Model schema
92
96
 
@@ -94,28 +98,52 @@ Examples: [database](examples/database.json), [model schema](examples/schema.jso
94
98
 
95
99
  ```json
96
100
  {
97
- "Country": {
98
- "collection": "countries",
99
- "api": ["openapi", "graphql"],
100
- "fields": {
101
- "id": { "type": "string", "primary": true, "generated": "uuid" },
102
- "name": { "type": "string", "required": true },
103
- "users": { "type": "User[]", "target": "countryId" }
104
- }
105
- },
106
- "User": {
107
- "collection": "users",
108
- "api": ["openapi", "graphql"],
109
- "fields": {
110
- "id": { "type": "string", "primary": true, "generated": "uuid" },
111
- "fullName": { "type": "string", "required": true },
112
- "country": { "type": "Country", "source": "countryId" }
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
- `api` defaults to `["openapi", "graphql"]`. `[]` excludes the model from both exports and GraphQL, while REST remains available. Related models must enable the same export format. Names must be valid identifiers and generated type/operation names must not collide. `and`, `or`, `not` are reserved filter names.
146
+ Model definitions belong in `models`. The schema root can define `api`, `timestamps` and `softDelete`; a model can override each setting. API precedence is model → schema root → configuration sections. Arrays replace the inherited value; `[]` excludes a model from GraphQL and OpenAPI while REST remains available. An explicit list does not activate an absent module. Related models must allow the same format. Model names must be valid identifiers; type and operation collisions cause errors. `and`, `or` and `not` are reserved filter names.
119
147
 
120
148
  | Capability | With schema | Without schema |
121
149
  |---|---|---|
@@ -252,10 +280,7 @@ const scope = [
252
280
  fullName: true,
253
281
  movies: [
254
282
  { id: true, title: true },
255
- {
256
- order: [{ field: 'title', direction: 'ASC' }],
257
- pager: { page: 1, pageSize: 5 },
258
- },
283
+ { order: [{ field: 'title', direction: 'ASC' }], pager: { page: 1, pageSize: 5 } },
259
284
  ],
260
285
  },
261
286
  {
@@ -346,10 +371,10 @@ mutation {
346
371
 
347
372
  ### GraphQL
348
373
 
349
- Set `database.schema` and enable `graphql.enabled: true` in the configuration, or start the server with `--graphql`:
374
+ Set `database.schema` and add `graphql: {}` to the configuration:
350
375
 
351
376
  ```sh
352
- npx deep-json-server --graphql server.config.js
377
+ npx deep-json-server server.config.js
353
378
  ```
354
379
 
355
380
  Send requests to `/graphql` using POST with `Content-Type: application/json` and a body of `{ "query": "…", "variables": {} }`. Change the path through `graphql.endpoint`.
@@ -386,35 +411,33 @@ Errors include `extensions.code`: `INVALID_INPUT`, `INVALID_QUERY`, `NOT_FOUND`,
386
411
 
387
412
  ## OpenAPI and schema exports
388
413
 
389
- Exports use OpenAPI 3.0.3. The purpose of each `scope` array position is described in text; the server validates their order.
390
-
391
- To serve the specification over HTTP, set `database.schema` and enable `openapi.enabled: true`, or start the server with `--openapi`. The JSON document is available at `/openapi.json` by default; change the path through `openapi.endpoint`. Open it in a separately installed Swagger UI or import it into an API client.
392
-
393
- To generate schemas, specify the format and configuration file:
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
- ```
414
+ Exports use OpenAPI 3.0.3. Add `openapi: {}` and `database.schema` to serve the specification at `/openapi.json`. Change the route with `openapi.endpoint`. Open the document in Swagger UI or import it into an API client.
400
415
 
401
- Generation runs without starting the server or reading database records. The command reads `database.schema` and writes schemas to `openapi.path` and `graphql.path`. The `enabled` flags control HTTP endpoints and are not required for exports. A configuration for generation only can contain:
416
+ Set output paths to save schemas:
402
417
 
403
418
  ```js
404
419
  export default {
405
- database: { schema: './schema.json' },
420
+ storage: 'file',
421
+ database: { source: './database.json', schema: './schema.json' },
406
422
  openapi: { path: './generated/openapi.yaml' },
407
423
  graphql: { path: './generated/schema.graphql' },
408
424
  };
409
425
  ```
410
426
 
411
- Each format needs its own output file. The command rejects destinations that would overwrite the configuration, database, schema, auth users or file metadata.
427
+ ```bash
428
+ npx deep-json-server server.config.js --generate-only
429
+ npx deep-json-server server.config.js --generate
430
+ ```
431
+
432
+ `--generate-only` exports and exits; `--generate` starts the server after exporting. Configuration sections select the formats. Each selected format requires its own `path`. Missing sections, missing destinations or generation errors fail the command before server startup.
433
+
434
+ Export reads only the schema. Database records, auth users and file storage are not opened; configuration values are still validated. The schema is loaded once, including when export is followed by startup. All destinations and selected schemas are checked before writing. Outputs cannot overwrite the configuration, database, schema, users, counters or file metadata. A disk write failure can leave an already saved file from another format.
412
435
 
413
436
  ## Authentication
414
437
 
415
- The `auth` section enables REST login and protects record changes in REST and GraphQL. Reads and all file operations remain public.
438
+ The `auth` section enables registration, login and permission checks for record changes in REST and GraphQL. Reads and all file operations remain public.
416
439
 
417
- Create an auth user in a separate file with `setup-auth.mjs`:
440
+ Define the first administrator in the initial data. For example, create `auth.json` with `setup-auth.mjs`:
418
441
 
419
442
  ```js
420
443
  import { writeFile } from 'node:fs/promises';
@@ -422,64 +445,99 @@ import { hashPassword } from '@kollors/deep-json-server/auth';
422
445
 
423
446
  const password = process.env.DJS_PASSWORD;
424
447
  if (!password) throw new Error('Set DJS_PASSWORD');
425
- await writeFile('./auth.json', JSON.stringify([
426
- { id: '1', username: 'admin', passwordHash: await hashPassword(password), isAdmin: true },
427
- ], null, 2), { flag: 'wx', mode: 0o600 });
448
+ await writeFile(
449
+ './auth.json',
450
+ JSON.stringify(
451
+ [{ id: '1', username: 'admin', passwordHash: await hashPassword(password), isAdmin: true }],
452
+ null,
453
+ 2,
454
+ ),
455
+ { flag: 'wx', mode: 0o600 },
456
+ );
428
457
  ```
429
458
 
430
459
  Set `DJS_PASSWORD` and run `node setup-auth.mjs`. Add the file to your server configuration:
431
460
 
432
461
  ```js
433
462
  export default {
434
- database: { path: './database.json' },
435
- auth: { users: './auth.json', expiresIn: 3600 },
463
+ storage: 'file',
464
+ database: { source: './database.json' },
465
+ auth: { source: './auth.json', expiresIn: 3600 },
436
466
  };
437
467
  ```
438
468
 
439
- Start with `npx deep-json-server server.config.js`. The presence of `auth` enables the module; omit the section to leave record changes open. Each user needs a unique string `id`, a unique `username` and a `passwordHash` created by the helper. `isAdmin` defaults to `false`. Passwords use salted scrypt hashes. Auth users are stored separately from database collections. The file is read at startup; restart the server after editing it.
440
-
441
- | REST request | Input | Response |
442
- |---|---|---|
443
- | `POST /auth/login` | JSON `{ "username": "admin", "password": "…" }` | `{ accessToken, expiresIn, user: { id, username, isAdmin } }` |
444
- | `GET /auth/me` | Bearer token | `{ id, username, isAdmin }` |
445
- | `POST /auth/logout` | Bearer token | `{ success: true }` |
469
+ Start with `npx deep-json-server server.config.js`. Each initial user needs a unique string `id`, a unique `username` and a `passwordHash` created by the helper. `isAdmin` defaults to `false`. Passwords use salted scrypt hashes.
446
470
 
447
- Pass the token in `Authorization: Bearer <accessToken>`. Incorrect credentials and invalid or expired tokens return HTTP 401. Sessions are kept in memory and disappear on restart; logout revokes the supplied token. Login can return HTTP 429 when the server has too many concurrent login attempts or active sessions.
471
+ With `storage: 'memory'`, pass an array in `auth.source`:
448
472
 
449
- OpenAPI describes Bearer authentication for record changes, `/auth/me` and `/auth/logout`. In Swagger UI, paste a token from login into **Authorize**. For schema exports, enable auth in the configuration and run `generate openapi server.config.js`; the users file is not read during generation. GraphQL and OpenAPI require `database.schema`.
450
-
451
- ## Record dates, deletion and ownership
473
+ ```js
474
+ import { hashPassword } from '@kollors/deep-json-server/auth';
452
475
 
453
- Configure defaults in `database`:
476
+ const password = process.env.DJS_PASSWORD;
477
+ if (!password) throw new Error('Set DJS_PASSWORD');
454
478
 
455
- ```js
456
479
  export default {
457
- database: {
458
- path: './database.json',
459
- schema: './schema.json',
460
- timestamps: true,
461
- softDelete: true,
480
+ storage: 'memory',
481
+ database: { source: { items: [] } },
482
+ auth: {
483
+ source: [
484
+ { id: '1', username: 'admin', passwordHash: await hashPassword(password), isAdmin: true },
485
+ ],
462
486
  },
463
- auth: { users: './auth.json' },
464
487
  };
465
488
  ```
466
489
 
467
- Each model can override `timestamps` and `softDelete` beside `collection` and `fields`. An omitted setting inherits the global value; `true` or `false` overrides it. CLI flags override global configuration, and model settings take priority over both. Without a schema, global settings apply to every collection. This model disables timestamps and keeps deleted records regardless of the global settings:
490
+ Auth users are stored separately from database collections. With `storage: 'file'`, registration, password changes and admin changes are saved to `auth.source` through `lowdb`. With `'memory'`, changes stay in an internal copy and disappear on restart; the original array is unchanged. A file write failure leaves users and sessions unchanged. The file is read at startup; restart after manual edits.
491
+
492
+ | REST request | JSON body | Response |
493
+ |---|---|---|
494
+ | `POST /auth/register` | `{ "username": "anna", "password": "…" }` | `201`: `{ id, username, isAdmin: false }` |
495
+ | `POST /auth/login` | `{ "username": "anna", "password": "…" }` | `{ accessToken, expiresIn, user: { id, username, isAdmin } }` |
496
+ | `GET /auth/me` | — | `{ id, username, isAdmin }` |
497
+ | `POST /auth/logout` | — | `{ success: true }` |
498
+ | `PATCH /auth/users/:id/password` | `{ "currentPassword": "…", "newPassword": "…" }` | `{ success: true }` |
499
+ | `PATCH /auth/users/:id/admin` | `{ "isAdmin": true }` | `{ id, username, isAdmin }` |
500
+
501
+ Registration and login are public. Send `Authorization: Bearer <accessToken>` for the other methods. Registration creates an ordinary user with a generated `id`; requests cannot include `id`, `passwordHash` or `isAdmin`. Usernames are case-sensitive and unique; duplicates return `409`. A username must contain a non-whitespace character and be at most 256 characters long. Passwords must contain 1–1024 characters. Values are not trimmed. Registration does not create a session: log in afterwards.
502
+
503
+ Users can change only their own password by supplying `currentPassword` and `newPassword`. Administrators follow the same rule for their own password. An administrator can change an ordinary user's password with just `newPassword`. Changing another administrator's password returns `403`. A successful password change ends all sessions of the target user, including the current session when changing your own password; log in again. An incorrect current password returns `401` without changing sessions.
504
+
505
+ Only administrators can change `isAdmin`. They can grant or remove another user's admin status. An administrator can remove their own status only if another administrator remains; otherwise the request returns `409`. This check accounts for concurrent requests. Existing tokens use the new permissions as soon as the change is saved, including for GraphQL mutations. An administrator may demote another administrator and then change their password as an ordinary user.
506
+
507
+ Invalid or expired tokens return `401`, insufficient permissions return `403`, and an absent user for an otherwise permitted operation returns `404`. Invalid request bodies return `400`. Login, registration and password changes may return `429` when too many password computations are running; login also limits active sessions. Sessions are kept in memory and disappear on restart. Logout revokes only the supplied token.
508
+
509
+ OpenAPI describes all auth routes and their Bearer token requirements. In Swagger UI, paste a token from login into **Authorize**. For schema exports, enable auth in the configuration and run `npx deep-json-server server.config.js --generate-only`; the users file is not read during generation. Auth methods are exposed through REST. GraphQL checks the same token when changing records. GraphQL and OpenAPI require `database.schema`.
510
+
511
+ ## Record dates, deletion and ownership
512
+
513
+ Set defaults at the schema root. Here `Note` inherits soft deletion and disables timestamps:
468
514
 
469
515
  ```json
470
516
  {
471
- "Note": {
472
- "collection": "notes",
473
- "timestamps": false,
474
- "softDelete": true,
475
- "fields": {
476
- "id": { "type": "string", "primary": true, "generated": "uuid" },
477
- "text": { "type": "string", "required": true }
517
+ "timestamps": true,
518
+ "softDelete": true,
519
+ "models": {
520
+ "Note": {
521
+ "collection": "notes",
522
+ "timestamps": false,
523
+ "fields": {
524
+ "id": {
525
+ "type": "string",
526
+ "primary": true,
527
+ "generated": "uuid"
528
+ },
529
+ "text": {
530
+ "type": "string",
531
+ "required": true
532
+ }
533
+ }
478
534
  }
479
535
  }
480
536
  }
481
537
  ```
482
538
 
539
+ Precedence: model → schema root → `false`. Explicit `false` disables an inherited setting. Without a schema, timestamps and soft deletion are disabled.
540
+
483
541
  | Field | Enabled by | Meaning |
484
542
  |---|---|---|
485
543
  | `createdAt`, `updatedAt` | `timestamps` | Creation and last update time |
@@ -516,8 +574,9 @@ Files are available through REST and documented in OpenAPI. Add storage to the c
516
574
 
517
575
  ```js
518
576
  export default {
519
- database: { path: './database.json' },
520
- files: { directory: './uploads', metadata: './files.json' },
577
+ storage: 'file',
578
+ database: { source: './database.json' },
579
+ files: { source: './uploads', metadata: './files.json' },
521
580
  };
522
581
  ```
523
582
 
@@ -527,7 +586,7 @@ Start the server:
527
586
  npx deep-json-server server.config.js
528
587
  ```
529
588
 
530
- For temporary tests, use `files.data` instead. Each initial record contains `name`, `mimeType`, binary `content` as a `Uint8Array`, and an optional `directory`. Uploaded files then remain in memory until the process exits.
589
+ For temporary tests, choose `storage: 'memory'` and pass an array in `files.source`. Each initial record contains `name`, `mimeType`, binary `content` as a `Uint8Array`, and an optional `directory`. Uploaded files then remain in memory until the process exits.
531
590
 
532
591
  Upload one file directly as the request body. `Content-Name` contains the URI-encoded file name, `Content-Type` contains its MIME type, and the optional `Content-Directory` contains the URI-encoded relative directory:
533
592
 
@@ -578,9 +637,9 @@ Content-Type: application/json
578
637
  }
579
638
  ```
580
639
 
581
- `PATCH` returns the updated metadata with status `200`; if a file already exists at the new path, the server returns `409`. `DELETE` returns `204` without a response body. A missing file returns `404` on every path-based operation. File paths in URLs are relative to `files.directory`, and all returned URLs are relative to the server origin.
640
+ `PATCH` returns the updated metadata with status `200`; if a file already exists at the new path, the server returns `409`. `DELETE` returns `204` without a response body. A missing file returns `404` on every path-based operation. File paths in URLs are relative to `files.source`, and all returned URLs are relative to the server origin.
582
641
 
583
- In disk mode, the binary is stored at `<files.directory>/<directory>/<name>`. Metadata stores `directory`, `mimeType` and `name`; the server reads the size from the file and builds its URLs. Directories and the metadata file are created when needed.
642
+ In disk mode, the binary is stored at `<files.source>/<directory>/<name>`. Metadata stores `directory`, `mimeType` and `name`; the server reads the size from the file and builds its URLs. Directories and the metadata file are created when needed.
584
643
 
585
644
  Use one server process per disk database and file store. Stop it before editing stored files or metadata manually. Storage paths cannot contain symbolic links. Uploads and renames cannot overwrite the database, counters, schema, auth users, loaded configuration or metadata file.
586
645
 
@@ -600,7 +659,7 @@ await server.listen();
600
659
 
601
660
  The `openapi()` and `graphql()` methods return schemas and require `database.schema`. `fastify()` returns the server instance for configuration and startup. The database and enabled services initialize on `ready()`, `listen()` or the first `inject()`; initialization errors stop startup.
602
661
 
603
- The second argument overrides module settings, for example `createServer(config, { files: false, graphql: true })`. Supported flags are `files`, `graphql`, `openapi` and `auth`. Enabling auth or files requires the corresponding configuration section; GraphQL and OpenAPI require a model schema.
662
+ `createServer(config)` takes one argument. Configuration sections control modules exactly as in the CLI. The `openapi()` and `graphql()` methods require their respective sections. They return schemas without writing files.
604
663
 
605
664
  The root import `@kollors/deep-json-server` also provides these functions. Server adapters load when enabled. Generators can be used independently:
606
665
 
@@ -614,7 +673,7 @@ await writeOpenapi(document, './generated/openapi.yaml');
614
673
  await writeGraphql(sdl, './generated/schema.graphql');
615
674
  ```
616
675
 
617
- Both generators accept `timestamps`, `softDelete` and `auth` options for the record fields. With `{ auth: true }`, OpenAPI also includes REST auth routes and mutation security requirements. `hashPassword()` is also available from the root package.
676
+ Standalone generators accept a schema path or object without a server configuration. The selected function supplies the default format; schema and model `api` settings can restrict it. Timestamps and soft deletion come from the schema. `{ auth: true }` adds ownership fields; OpenAPI also describes auth routes and token requirements. `hashPassword()` is available from the root package.
618
677
 
619
678
  `generateOpenapi()` also accepts `host`, `port`, `pageSize`, `maxPageSize` and `info`. Pass a schema object instead of a path if preferred. Servers and generators use their own copy of the model. Pagination sizes must be positive integers; `pageSize` cannot exceed `maxPageSize`.
620
679