db-model-router 1.0.4 → 1.0.5

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 (92) hide show
  1. package/README.md +110 -16
  2. package/TODO.md +14 -0
  3. package/dbmr.schema.json +333 -0
  4. package/demo/.dockerignore +7 -0
  5. package/demo/.env.example +13 -0
  6. package/demo/Dockerfile +20 -0
  7. package/demo/app.js +37 -0
  8. package/demo/commons/add_migration.js +43 -0
  9. package/demo/commons/db.js +17 -0
  10. package/demo/commons/migrate.js +65 -0
  11. package/demo/commons/security.js +30 -0
  12. package/demo/commons/session.js +13 -0
  13. package/demo/dbmr.schema.json +362 -0
  14. package/demo/docs/llm.md +197 -0
  15. package/demo/llms.txt +70 -0
  16. package/demo/middleware/logger.js +67 -0
  17. package/demo/migrations/20260430155808_create_migrations_table.sql +6 -0
  18. package/demo/migrations/20260430155809_create_tables.sql +207 -0
  19. package/demo/models/addresses.js +22 -0
  20. package/demo/models/cart_items.js +18 -0
  21. package/demo/models/carts.js +16 -0
  22. package/demo/models/categories.js +20 -0
  23. package/demo/models/coupons.js +23 -0
  24. package/demo/models/order_items.js +21 -0
  25. package/demo/models/orders.js +25 -0
  26. package/demo/models/payments.js +21 -0
  27. package/demo/models/product_images.js +18 -0
  28. package/demo/models/product_reviews.js +20 -0
  29. package/demo/models/product_variants.js +20 -0
  30. package/demo/models/products.js +30 -0
  31. package/demo/models/shipments.js +19 -0
  32. package/demo/models/users.js +19 -0
  33. package/demo/models/wishlists.js +15 -0
  34. package/demo/openapi.json +5872 -0
  35. package/demo/package-lock.json +2810 -0
  36. package/demo/package.json +34 -0
  37. package/demo/routes/addresses.js +6 -0
  38. package/demo/routes/carts/cart_items.js +7 -0
  39. package/demo/routes/carts.js +6 -0
  40. package/demo/routes/categories.js +6 -0
  41. package/demo/routes/coupons.js +6 -0
  42. package/demo/routes/docs.js +18 -0
  43. package/demo/routes/health.js +35 -0
  44. package/demo/routes/index.js +39 -0
  45. package/demo/routes/orders/order_items.js +7 -0
  46. package/demo/routes/orders/payments.js +7 -0
  47. package/demo/routes/orders/shipments.js +7 -0
  48. package/demo/routes/orders.js +6 -0
  49. package/demo/routes/products/product_images.js +7 -0
  50. package/demo/routes/products/product_reviews.js +7 -0
  51. package/demo/routes/products/product_variants.js +7 -0
  52. package/demo/routes/products.js +6 -0
  53. package/demo/routes/users.js +6 -0
  54. package/demo/routes/wishlists.js +6 -0
  55. package/docker-compose.yml +1 -1
  56. package/package.json +8 -7
  57. package/scripts/demo-create.js +47 -0
  58. package/skill/SKILL.md +464 -0
  59. package/skill/references/cockroachdb.md +49 -0
  60. package/skill/references/dynamodb.md +53 -0
  61. package/skill/references/mongodb.md +56 -0
  62. package/skill/references/mssql.md +55 -0
  63. package/skill/references/oracle.md +52 -0
  64. package/skill/references/postgres.md +50 -0
  65. package/skill/references/redis.md +53 -0
  66. package/skill/references/sqlite3.md +43 -0
  67. package/src/cli/commands/generate.js +58 -17
  68. package/src/cli/commands/help.js +11 -6
  69. package/src/cli/commands/init.js +2 -2
  70. package/src/cli/commands/inspect.js +1 -0
  71. package/src/cli/diff-engine.js +52 -22
  72. package/src/cli/generate-docs-route.js +31 -0
  73. package/src/cli/generate-migration.js +356 -0
  74. package/src/cli/generate-route.js +52 -24
  75. package/src/cli/init/dependencies.js +3 -0
  76. package/src/cli/init/generators.js +1 -1
  77. package/src/cli/init.js +8 -8
  78. package/src/cockroachdb/db.js +90 -59
  79. package/src/commons/route.js +20 -20
  80. package/src/commons/validator.js +58 -1
  81. package/src/dynamodb/db.js +50 -27
  82. package/src/mongodb/db.js +1 -0
  83. package/src/mssql/db.js +89 -61
  84. package/src/mysql/db.js +1 -0
  85. package/src/oracle/db.js +1 -0
  86. package/src/postgres/db.js +61 -41
  87. package/src/redis/db.js +1 -0
  88. package/src/schema/schema-parser.js +43 -1
  89. package/src/schema/schema-printer.js +7 -0
  90. package/src/schema/schema-validator.js +17 -0
  91. package/src/sqlite3/db.js +1 -0
  92. package/docs/SKILL.md +0 -419
package/docs/SKILL.md DELETED
@@ -1,419 +0,0 @@
1
- ---
2
- name: db-model-router
3
- description: Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 10 adapters, identical API. Works with express or ultimate-express.
4
- ---
5
-
6
- # db-model-router — LLM Skill Reference
7
-
8
- Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 10 adapters, identical API. Works with `express` or `ultimate-express`. Generated projects use ESM (`import`/`export`).
9
-
10
- ## LLM Workflow (follow this order)
11
-
12
- 1. **Scaffold**: `db-model-router init --framework express --database postgres --session redis --rateLimiting --helmet --logger --yes` (all flags → zero prompts)
13
- 2. **Start infra**: `npm run docker:up` (starts DB + CloudBeaver + optional Loki/Grafana)
14
- 3. **Migrations**: Write SQL/JS migration files into `migrations/`, then `npm run migrate`
15
- 4. **Generate models**: `db-model-router generate --from dbmr.schema.json --models`
16
- 5. **Generate routes + tests**: `db-model-router generate --from dbmr.schema.json --routes --tests`
17
- 6. **Run**: `npm run dev`
18
-
19
- Step 1 creates the project with ESM, Docker, and all infrastructure. Step 2 starts containers. Steps 3-5 define schema and generate code. Step 6 starts the server.
20
-
21
- ## Install
22
-
23
- ```bash
24
- npm install db-model-router <framework> <driver>
25
- ```
26
-
27
- Frameworks: `express` or `ultimate-express` (auto-detected, prefers ultimate-express)
28
- Drivers: `mysql2`, `pg`, `better-sqlite3`, `mongodb`, `mssql`, `oracledb`, `ioredis`, `@aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb`
29
-
30
- ## Adapters
31
-
32
- | Module Key | Driver | Default Port |
33
- | ------------- | ------------------------------------------------ | ------------ |
34
- | `mysql` | mysql2 | 3306 |
35
- | `mariadb` | mysql2 | 3306 |
36
- | `postgres` | pg | 5432 |
37
- | `sqlite3` | better-sqlite3 | — |
38
- | `mongodb` | mongodb | 27017 |
39
- | `mssql` | mssql | 1433 |
40
- | `cockroachdb` | pg | 26257 |
41
- | `oracle` | oracledb | 1521 |
42
- | `redis` | ioredis | 6379 |
43
- | `dynamodb` | @aws-sdk/client-dynamodb + @aws-sdk/lib-dynamodb | — |
44
-
45
- ## Init → Connect → Model → Route
46
-
47
- ```js
48
- const { init, db, model, route } = require("db-model-router");
49
- init("postgres"); // mysql|postgres|sqlite3|mongodb|mssql|cockroachdb|oracle|redis|dynamodb
50
- db.connect({ host, port: 5432, user, password, database });
51
-
52
- const users = model(
53
- db,
54
- "users",
55
- {
56
- name: "required|string",
57
- email: "required|string",
58
- age: "integer",
59
- meta: "object",
60
- },
61
- "id",
62
- ["email"],
63
- {
64
- safeDelete: "is_deleted",
65
- created_at: "created_at",
66
- modified_at: "updated_at",
67
- },
68
- );
69
-
70
- app.use("/users", route(users));
71
- ```
72
-
73
- **Critical**: call `init()` before `db.connect()`. Default adapter is mysql. Do NOT destructure `db` before `init()`.
74
-
75
- ## model(db, table, structure, pk, unique, option)
76
-
77
- | Param | Type | Description |
78
- | --------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
79
- | structure | `{col: "rule"}` | Types: `string\|integer\|numeric\|boolean\|object\|datetime\|auto_increment`. Prefix `required\|` for NOT NULL. PK, timestamps, soft-delete cols are auto-excluded by the code generator |
80
- | pk | string | Primary key column. Convention: `<table>_id` |
81
- | unique | string[] | Columns for upsert conflict resolution |
82
- | option | object | `{ safeDelete, created_at, modified_at }` — column names or null |
83
-
84
- ## Model Methods (all async)
85
-
86
- ```js
87
- // INSERT — single returns record, bulk returns {rows, message, type}
88
- await m.insert({ name: "Alice", email: "a@b.com", age: 30 }) // → {id:1, name:"Alice", ...}
89
- await m.insert({ data: [{...}, {...}] }) // → {rows:2, message, type:"success"}
90
-
91
- // UPDATE — PK required in payload
92
- await m.update({ id: 1, name: "Alice V2", email: "a@b.com", age: 31 })
93
- await m.update({ data: [{id:1,...}, {id:2,...}] })
94
-
95
- // PATCH — partial update, only sends changed fields, PK required
96
- await m.patch({ id: 1, age: 35 }) // → full merged record
97
-
98
- // UPSERT — PK optional, uses unique cols for conflict
99
- await m.upsert({ email: "new@b.com", name: "New", age: 20 })
100
-
101
- // READ
102
- await m.byId(1) // → record or null
103
- await m.find({ name: "Alice" }) // → {data:[], count}
104
- await m.findOne({ email: "a@b.com" }) // → record or false
105
- await m.list({ page: 0, size: 10, sort: ["-age"] }) // → {data:[], count}
106
-
107
- // DELETE — with safeDelete: sets column=1; without: hard delete
108
- await m.remove(1)
109
- await m.remove({ name: "Bob" })
110
- ```
111
-
112
- ## Filter Syntax
113
-
114
- Structure: `[OR_groups[AND_conditions[col, op, val]]]`
115
- Ops: `= != < > <= >= like not like in not in`
116
-
117
- ```js
118
- // AND: age > 25 AND type = 1
119
- [
120
- [
121
- ["age", ">", 25],
122
- ["type", "=", 1],
123
- ],
124
- ][
125
- // OR: name = "A" OR name = "B"
126
- ([["name", "=", "A"]], [["name", "=", "B"]])
127
- ][
128
- // IN
129
- [["type", "in", [1, 2, 3]]]
130
- ][
131
- // LIKE (auto-wraps with %)
132
- [["name", "like", "Ali"]]
133
- ];
134
- ```
135
-
136
- ## route(model, override?)
137
-
138
- Generates Express Router with 9 endpoints:
139
-
140
- | Method | Path | Action |
141
- | ------ | ------ | -------------------------------- |
142
- | GET | `/:pk` | Get by PK |
143
- | POST | `/add` | Insert single |
144
- | PUT | `/:pk` | Update single (PK from URL) |
145
- | PATCH | `/:pk` | Partial update (PK from URL) |
146
- | DELETE | `/:pk` | Delete single |
147
- | GET | `/` | List (page, size, sort, filters) |
148
- | POST | `/` | Bulk insert `{data:[...]}` |
149
- | PUT | `/` | Bulk update `{data:[...]}` |
150
- | DELETE | `/` | Bulk delete |
151
-
152
- **Payload override** (multi-tenancy): `route(m, { tenant_id: "user.tenant_id" })` — maps cols to `req` paths via lodash.get.
153
-
154
- **Query params**: `select_columns=name,email`, `output_content_type=csv|xml|json`, `sort=-age,name`
155
-
156
- ## CLI Tools
157
-
158
- ### Unified CLI: `db-model-router`
159
-
160
- ```bash
161
- db-model-router <command> [options]
162
- db-model-router help <command> # detailed help for any command
163
- ```
164
-
165
- #### `init` — Project Scaffold
166
-
167
- Scaffolds a complete ESM-based Express REST API project with Docker support.
168
-
169
- ```bash
170
- # Fully non-interactive (LLM-friendly — zero prompts)
171
- db-model-router init --framework express --database postgres --session redis \
172
- --rateLimiting --helmet --logger --yes
173
-
174
- # With Loki logging + Grafana
175
- db-model-router init --database postgres --logger --loki --yes
176
-
177
- # With output directory
178
- db-model-router init --database mysql --output backend --yes
179
-
180
- # From schema file
181
- db-model-router init --from dbmr.schema.json --yes --no-install
182
- ```
183
-
184
- | Flag | Description | Default |
185
- | -------------------- | ----------------------------------------------------------------------------------------------------------- | ---------- |
186
- | `--from <path>` | Read config from schema file | |
187
- | `--framework <name>` | `express` or `ultimate-express` | (prompted) |
188
- | `--database <name>` | `mysql`, `mariadb`, `postgres`, `sqlite3`, `mongodb`, `mssql`, `cockroachdb`, `oracle`, `redis`, `dynamodb` | (prompted) |
189
- | `--db <name>` | Alias for `--database` | |
190
- | `--session <type>` | `memory`, `redis`, `database` | (prompted) |
191
- | `--output <dir>` | Backend source directory (relative to cwd) | (root) |
192
- | `--rateLimiting` | Enable rate limiting | yes |
193
- | `--helmet` | Enable Helmet security headers | yes |
194
- | `--logger` | Enable Winston request logger | yes |
195
- | `--loki` | Enable Loki transport + Loki/Grafana in docker-compose | no |
196
-
197
- Generated files (ESM, `"type": "module"`):
198
-
199
- ```
200
- app.js Express entry point
201
- .env / .env.example Environment config (random passwords)
202
- Dockerfile node:alpine production image
203
- docker-compose.yml DB + CloudBeaver + optional Loki/Grafana
204
- .cloudbeaver/data-sources.json Auto-connects CloudBeaver to DB
205
- .grafana/datasources.yml Auto-connects Grafana to Loki (when --loki)
206
- <output>/commons/db.js Database init, connect, global.db
207
- <output>/commons/session.js Session configuration
208
- <output>/commons/security.js Helmet, rate limiting, custom headers
209
- <output>/commons/migrate.js Migration runner (importable + standalone)
210
- <output>/commons/add_migration.js Migration creator (importable + standalone)
211
- <output>/middleware/logger.js Winston logger (+ Loki when LOKI_HOST is set)
212
- <output>/route/index.js Central route mounting
213
- <output>/route/health.js GET /health with DB connectivity check
214
- <output>/migrations/ Initial migration files
215
- ```
216
-
217
- Docker services (auto-generated in docker-compose.yml):
218
-
219
- | Service | When | Port | Notes |
220
- | ----------- | --------------------------- | ------ | ------------------------------------- |
221
- | Database | Always (except sqlite3) | Varies | Random password, bind mount `./data/` |
222
- | Redis | `session=redis`, DB ≠ redis | 6379 | Session store |
223
- | CloudBeaver | SQL/MongoDB databases | 8978 | Web DB admin, auto-connected |
224
- | Loki | `--loki` | 3100 | Log aggregation |
225
- | Grafana | `--loki` | 3001 | Log visualization |
226
-
227
- Scripts: `start`, `dev`, `test`, `migrate`, `add_migration`, `docker:build`, `docker:up`, `docker:down`.
228
-
229
- **Critical**: `db-model-router` is CJS. Generated ESM code must use: `import dbModelRouter from "db-model-router"; const { init, db } = dbModelRouter;` — NOT named imports.
230
-
231
- #### `inspect` — DB Introspection
232
-
233
- ```bash
234
- db-model-router inspect --type postgres --env .env [--out schema.json] [--tables t1,t2]
235
- ```
236
-
237
- | Flag | Description |
238
- | ------------------ | --------------------------------------- |
239
- | `--type <adapter>` | Database adapter (required) |
240
- | `--env <path>` | Path to .env file |
241
- | `--out <path>` | Output file (default: dbmr.schema.json) |
242
- | `--tables <list>` | Comma-separated table filter |
243
-
244
- #### `generate` — Code Generation
245
-
246
- ```bash
247
- db-model-router generate --from dbmr.schema.json [--models] [--routes] [--openapi] [--tests] [--llm-docs]
248
- ```
249
-
250
- | Flag | Description |
251
- | --------------- | --------------------------------------- |
252
- | `--from <path>` | Schema file (default: dbmr.schema.json) |
253
- | `--models` | Generate only model files |
254
- | `--routes` | Generate only route files + index |
255
- | `--openapi` | Generate only OpenAPI spec |
256
- | `--tests` | Generate only test files |
257
- | `--llm-docs` | Generate only LLM docs |
258
-
259
- No flags = generate all. Generated routes/tests use ESM imports.
260
-
261
- #### `doctor` — Validation
262
-
263
- ```bash
264
- db-model-router doctor [--from dbmr.schema.json] [--json]
265
- ```
266
-
267
- Checks: schema validation, dependency check, file sync.
268
-
269
- #### `diff` — Preview Changes
270
-
271
- ```bash
272
- db-model-router diff [--from dbmr.schema.json] [--json]
273
- ```
274
-
275
- Shows added/modified/deleted files without writing.
276
-
277
- #### Universal Flags (all commands)
278
-
279
- `--yes`, `--json`, `--dry-run`, `--no-install`, `--help`
280
-
281
- ## Connection Configs
282
-
283
- ```js
284
- // MySQL (default)
285
- db.connect({
286
- host,
287
- port: 3306,
288
- user,
289
- password,
290
- database,
291
- connectionLimit: 100,
292
- });
293
-
294
- // PostgreSQL / CockroachDB
295
- init("postgres"); // cockroachdb port: 26257
296
- db.connect({ host, port: 5432, user, password, database });
297
-
298
- // SQLite3
299
- init("sqlite3");
300
- db.connect({ database: "./file.db" }); // or ":memory:"
301
-
302
- // MongoDB
303
- init("mongodb");
304
- db.connect({ host, port: 27017, username, password, database });
305
- // or: db.connect({ uri: "mongodb://user:pass@host:27017/db" })
306
-
307
- // MSSQL
308
- init("mssql");
309
- await db.connect({
310
- server: host,
311
- port: 1433,
312
- user,
313
- password,
314
- database,
315
- options: { encrypt: false, trustServerCertificate: true },
316
- });
317
-
318
- // Oracle
319
- init("oracle");
320
- db.connect({ host, port: 1521, user, password, database });
321
-
322
- // Redis
323
- init("redis");
324
- db.connect({ host, port: 6379, password });
325
-
326
- // DynamoDB
327
- init("dynamodb");
328
- db.connect({ region, endpoint, accessKeyId, secretAccessKey });
329
- ```
330
-
331
- ## Rules
332
-
333
- 1. `init()` before `db.connect()`. Don't destructure `db` before `init()` — it's a getter.
334
- 2. Generated projects are ESM (`"type": "module"`). The library itself is CJS. Use default import: `import dbModelRouter from "db-model-router"; const { init, db } = dbModelRouter;`
335
- 3. `model structure` excludes: PK col, timestamp cols, soft-delete cols.
336
- 4. `update()`/`patch()` require PK in payload. `upsert()` PK is optional.
337
- 5. `findOne()` returns `false` on no match. `byId()` returns `null`.
338
- 6. Bulk ops wrap in `{ data: [...] }`. Single ops use flat object.
339
- 7. Timestamps auto-stripped from payloads. DB handles defaults/triggers.
340
- 8. `safeDelete` makes `remove()` soft-delete; all reads auto-filter deleted rows.
341
- 9. `list()` defaults: page=0, size=30. `sort` array: `["-col"]` for DESC.
342
- 10. Use the unified `db-model-router` CLI with `dbmr.schema.json` for new projects.
343
- 11. `generate` auto-generates test files alongside routes. Tests use `supertest`.
344
- 12. `global.db` is set by `commons/db.js` — accessible anywhere without imports.
345
- 13. Logger dynamically loads `winston-loki` only when `LOKI_HOST` env var is set.
346
- 14. Docker passwords are randomly generated and shared between `.env` and `docker-compose.yml`.
347
-
348
- ## Environment Variables by Database
349
-
350
- | Database | Variables |
351
- | ----------- | ---------------------------------------------------------------------- |
352
- | mysql | `PORT DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
353
- | mariadb | `PORT DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
354
- | postgres | `PORT DB_HOST DB_PORT=5432 DB_NAME DB_USER DB_PASS` |
355
- | cockroachdb | `PORT DB_HOST DB_PORT=26257 DB_NAME DB_USER DB_PASS` |
356
- | sqlite3 | `PORT DB_NAME=./data/data.db` |
357
- | mongodb | `PORT DB_HOST DB_PORT=27017 DB_NAME DB_USER DB_PASS` |
358
- | mssql | `PORT DB_HOST DB_PORT=1433 DB_NAME DB_USER DB_PASS` |
359
- | oracle | `PORT DB_HOST DB_PORT=1521 DB_NAME DB_USER DB_PASS` |
360
- | redis | `PORT DB_HOST DB_PORT=6379 DB_PASS` |
361
- | dynamodb | `PORT AWS_REGION AWS_ENDPOINT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY` |
362
-
363
- When `session=redis` and database ≠ redis: adds `REDIS_HOST REDIS_PORT REDIS_PASS`.
364
- When `logger=true`: adds `APP_NAME LOG_LEVEL LOKI_HOST` (LOKI_HOST empty unless `--loki`).
365
-
366
- ## Schema-Driven Workflow
367
-
368
- The `db-model-router` command uses `dbmr.schema.json` as the source of truth.
369
-
370
- ### LLM Workflow (schema-driven)
371
-
372
- 1. **Inspect** (existing DB): `db-model-router inspect --type postgres --env .env` → writes `dbmr.schema.json`
373
- 2. **Edit** `dbmr.schema.json` — add relationships, tweak columns, set options
374
- 3. **Generate**: `db-model-router generate --from dbmr.schema.json` → models, routes, tests, OpenAPI
375
- 4. **Doctor**: `db-model-router doctor --from dbmr.schema.json` → validate schema + check sync
376
- 5. **Run**: `npm run dev`
377
-
378
- ### dbmr.schema.json Format
379
-
380
- ```json
381
- {
382
- "adapter": "postgres",
383
- "framework": "express",
384
- "options": {
385
- "session": "redis",
386
- "rateLimiting": true,
387
- "helmet": true,
388
- "logger": true,
389
- "loki": false
390
- },
391
- "tables": {
392
- "users": {
393
- "columns": {
394
- "user_id": "auto_increment",
395
- "name": "required|string",
396
- "email": "required|string",
397
- "age": "integer",
398
- "is_deleted": "boolean",
399
- "created_at": "datetime",
400
- "updated_at": "datetime"
401
- },
402
- "pk": "user_id",
403
- "unique": ["email"],
404
- "softDelete": "is_deleted",
405
- "timestamps": { "created_at": "created_at", "modified_at": "updated_at" }
406
- }
407
- },
408
- "relationships": [
409
- { "parent": "users", "child": "posts", "foreignKey": "user_id" }
410
- ]
411
- }
412
- ```
413
-
414
- Fields per table: `columns` (required, include ALL columns), `pk` (required, convention: `<table>_id`), `unique` (default `[pk]`), `softDelete`, `timestamps`.
415
- Column rules: `(required|)?(string|integer|numeric|boolean|object|datetime|auto_increment)`.
416
-
417
- - `auto_increment` — auto-incrementing PK (SERIAL in Postgres, AUTO_INCREMENT in MySQL/MariaDB)
418
- - `datetime` — date/time columns (TIMESTAMP, DATETIME, DATE)
419
- - `required|<type>` — NOT NULL constraint