db-model-router 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  A database-agnostic REST API generator for Node.js. Works with Express or ultimate-express (a high-performance drop-in replacement). Define a model, get a full CRUD API with filtering, pagination, and bulk operations — backed by any of 9 supported databases.
4
4
 
5
+ ## Build a REST API with AI
6
+
7
+ This library is designed to be driven by an AI assistant. Give it a prompt like this:
8
+
9
+ ```
10
+ Use db-model-router to build a REST API for a task management app with postgres.
11
+ I need: users (name, email, password_hash), projects (name, description, owner_id → users),
12
+ and tasks (title, status, priority, project_id → projects, assignee_id → users).
13
+ Scaffold the project, write the migrations, generate models and routes with parent-child
14
+ relationships for projects.tasks, and make sure everything runs.
15
+ ```
16
+
17
+ For the LLM skill reference, see [SKILL.md](./docs/SKILL.md).
18
+
5
19
  ## Supported Adapters
6
20
 
7
21
  | Adapter | Module Key | Driver | Install |
@@ -55,6 +69,143 @@ npm install db-model-router @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
55
69
 
56
70
  Both `express` and `ultimate-express` are optional peer dependencies. The library auto-detects which one is installed (preferring `ultimate-express` when both are present). All database drivers are also optional peer dependencies, so your `node_modules` stays lean.
57
71
 
72
+ ## Quick Start
73
+
74
+ The fastest way to start a new project:
75
+
76
+ ```bash
77
+ # Scaffold a project interactively
78
+ npx db-model-router init
79
+
80
+ # Or fully non-interactive from a schema file
81
+ db-model-router init --from dbmr.schema.json --yes --no-install
82
+ db-model-router generate --from dbmr.schema.json
83
+ ```
84
+
85
+ After scaffolding:
86
+
87
+ ```bash
88
+ # 1. Edit .env with your database credentials
89
+ # 2. Start developing
90
+ npm run dev
91
+ ```
92
+
93
+ ## Schema-Driven Workflow
94
+
95
+ Instead of running multiple CLI commands manually, you can define your entire project in a single `dbmr.schema.json` file and let the CLI generate everything from it.
96
+
97
+ ### The Schema File
98
+
99
+ `dbmr.schema.json` is a declarative JSON file that describes your adapter, framework, tables, columns, relationships, and options — all in one place:
100
+
101
+ ```json
102
+ {
103
+ "adapter": "postgres",
104
+ "framework": "express",
105
+ "options": {
106
+ "session": "redis",
107
+ "rateLimiting": true,
108
+ "helmet": true,
109
+ "logger": true
110
+ },
111
+ "tables": {
112
+ "users": {
113
+ "columns": {
114
+ "name": "required|string",
115
+ "email": "required|string",
116
+ "age": "integer",
117
+ "is_deleted": "boolean"
118
+ },
119
+ "pk": "id",
120
+ "unique": ["email"],
121
+ "softDelete": "is_deleted",
122
+ "timestamps": {
123
+ "created_at": "created_at",
124
+ "modified_at": "updated_at"
125
+ }
126
+ },
127
+ "posts": {
128
+ "columns": {
129
+ "title": "required|string",
130
+ "body": "string",
131
+ "user_id": "required|integer"
132
+ },
133
+ "pk": "id",
134
+ "unique": ["id"]
135
+ }
136
+ },
137
+ "relationships": [
138
+ { "parent": "users", "child": "posts", "foreignKey": "user_id" }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ Table entries support these fields:
144
+
145
+ | Field | Required | Description |
146
+ | ------------ | -------- | -------------------------------------------------------------- |
147
+ | `columns` | Yes | Object mapping column names to Column_Rule strings |
148
+ | `pk` | No | Primary key column name (defaults to `"id"`) |
149
+ | `unique` | No | Array of unique constraint columns (defaults to `[pk]`) |
150
+ | `softDelete` | No | Column name used for soft-delete |
151
+ | `timestamps` | No | Object with `created_at` and `modified_at` column name mapping |
152
+
153
+ Column rules use the format `(required|)?(string|integer|numeric|boolean|object)` — e.g. `"required|string"`, `"integer"`, `"object"`.
154
+
155
+ ### Unified CLI: `db-model-router`
156
+
157
+ The `db-model-router` command is the unified entry point with five subcommands:
158
+
159
+ ```bash
160
+ db-model-router <subcommand> [flags]
161
+ ```
162
+
163
+ | Subcommand | Description |
164
+ | ---------- | ------------------------------------------------------------------- |
165
+ | `init` | Scaffold a new project (optionally from a schema file) |
166
+ | `inspect` | Introspect a live database and produce a `dbmr.schema.json` |
167
+ | `generate` | Generate models, routes, tests, and OpenAPI spec from the schema |
168
+ | `doctor` | Validate schema, check dependencies, verify generated files in sync |
169
+ | `diff` | Preview what changes regeneration would make (read-only) |
170
+
171
+ #### Universal Flags
172
+
173
+ All subcommands accept these flags:
174
+
175
+ | Flag | Description |
176
+ | -------------- | ----------------------------------------------------------- |
177
+ | `--yes` | Accept all defaults, suppress interactive prompts |
178
+ | `--json` | Output machine-readable JSON instead of human-readable text |
179
+ | `--dry-run` | Preview actions without writing files or running commands |
180
+ | `--no-install` | Skip `npm install` (applies to commands that would run it) |
181
+ | `--help` | Show usage information for the subcommand |
182
+
183
+ #### Quick Workflow Example
184
+
185
+ ```bash
186
+ # 1. Introspect an existing database into a schema file
187
+ db-model-router inspect --type postgres --env .env
188
+
189
+ # 2. (Optional) Edit dbmr.schema.json to add relationships, tweak columns, etc.
190
+
191
+ # 3. Generate all artifacts from the schema
192
+ db-model-router generate --from dbmr.schema.json
193
+
194
+ # 4. Check everything is in sync
195
+ db-model-router doctor --from dbmr.schema.json
196
+
197
+ # 5. Preview what a regeneration would change
198
+ db-model-router diff --from dbmr.schema.json
199
+ ```
200
+
201
+ Or start a brand-new project from a schema file:
202
+
203
+ ```bash
204
+ # Scaffold project + generate everything in one go
205
+ db-model-router init --from dbmr.schema.json --yes --no-install
206
+ db-model-router generate --from dbmr.schema.json
207
+ ```
208
+
58
209
  ## MySQL Example
59
210
 
60
211
  ### 1. Connect
@@ -109,13 +260,14 @@ app.use("/users", route(users));
109
260
  app.listen(3000);
110
261
  ```
111
262
 
112
- This creates 8 endpoints:
263
+ This creates 9 endpoints:
113
264
 
114
265
  | Method | Path | Description |
115
266
  | ------ | ------------ | ------------------------------- |
116
267
  | GET | `/users/:id` | Get one record by PK |
117
268
  | POST | `/users/add` | Insert a single record |
118
269
  | PUT | `/users/:id` | Update a single record |
270
+ | PATCH | `/users/:id` | Partial update a single record |
119
271
  | DELETE | `/users/:id` | Delete a single record |
120
272
  | GET | `/users/` | List with pagination |
121
273
  | POST | `/users/` | Bulk insert (`{ data: [...] }`) |
@@ -274,207 +426,6 @@ The model and route APIs remain identical across all adapters. See the individua
274
426
  - [Redis](./docs/adapters/redis.md)
275
427
  - [DynamoDB](./docs/adapters/dynamodb.md)
276
428
 
277
- ## CLI Tools
278
-
279
- Three CLI commands are included to scaffold models, routes, and full apps from an existing database.
280
-
281
- ### generate-app
282
-
283
- The fastest way to go from database to running API. Scaffolds a complete Express REST API project in a single command — introspects your database, generates models and routes (including parent-child relationships), and creates the app entry point, middleware, environment config, and project structure.
284
-
285
- ```bash
286
- # Full app from MySQL
287
- db-model-router-generate-app --type mysql --env .env
288
-
289
- # SQLite3 into a specific directory
290
- db-model-router-generate-app --type sqlite3 --database ./myapp.db --output ./my-api
291
-
292
- # Postgres with specific tables and relationships
293
- db-model-router-generate-app --type postgres --env .env --tables users,posts,posts.comments
294
- ```
295
-
296
- Options:
297
-
298
- | Option | Description |
299
- | ------------ | ------------------------------------------------------------------------------- |
300
- | `--type` | Database type (mysql, postgres, sqlite3, mssql, oracle, cockroachdb) [required] |
301
- | `--output` | Output directory (default: current directory) |
302
- | `--host` | Database host |
303
- | `--port` | Database port |
304
- | `--database` | Database name or file path |
305
- | `--user` | Database user |
306
- | `--password` | Database password |
307
- | `--schema` | Schema name (postgres only) |
308
- | `--tables` | Comma-separated tables, supports `parent.child` notation for relationships |
309
- | `--env` | Path to .env file for DB connection |
310
-
311
- Generated project structure:
312
-
313
- ```
314
- my-api/
315
- app.js # Express app with init(), db.connect(), middleware, error handler, health check
316
- .env.example # Pre-filled environment template for your DB type
317
- .gitignore # node_modules, .env, *.db
318
- middleware/
319
- logger.js # Request logger (method, URL, status, response time)
320
- models/
321
- users.js # Auto-generated from DB introspection
322
- posts.js
323
- index.js
324
- routes/
325
- users.js # Auto-generated route files
326
- posts.js
327
- index.js
328
- openapi.json # OpenAPI 3.0 spec
329
- migrations/
330
- README.md # Placeholder for migration scripts
331
- sessions/
332
- README.md # Placeholder for session config
333
- ```
334
-
335
- When `--tables` includes parent-child notation (e.g., `posts.comments`), the routes directory also includes scoped child route files and nested route mounting — see [Parent-Child Relationships](#parent-child-relationships-foreign-keys) below.
336
-
337
- To start the generated app:
338
-
339
- ```bash
340
- cp .env.example .env # edit with your DB credentials
341
- npm install
342
- node app.js
343
- ```
344
-
345
- ### generate-model
346
-
347
- Connects to your database, introspects all tables, and generates model files with validation rules, primary keys, unique constraints, and auto-detected options (safeDelete, timestamps). This is called automatically by `generate-app`, but can be used standalone.
348
-
349
- ```bash
350
- # Basic usage
351
- db-model-router-generate-model --type mysql --host localhost --database mydb --user root --password secret
352
-
353
- # Using an .env file
354
- db-model-router-generate-model --type postgres --env .env --output ./src/models
355
-
356
- # SQLite3
357
- db-model-router-generate-model --type sqlite3 --database ./myapp.db --output ./models
358
-
359
- # Only specific tables
360
- db-model-router-generate-model --type mysql --env .env --tables users,posts,comments
361
- ```
362
-
363
- Options:
364
-
365
- | Option | Description |
366
- | ------------ | ----------------------------------------------------------------------------- |
367
- | `--type` | Database type (mysql, postgres, sqlite3, mssql, oracle, cockroachdb) |
368
- | `--host` | Database host (default: localhost) |
369
- | `--port` | Database port |
370
- | `--database` | Database name (or file path for sqlite3) |
371
- | `--user` | Database user |
372
- | `--password` | Database password |
373
- | `--schema` | Schema name (postgres only, default: public) |
374
- | `--output` | Output directory (default: ./models) |
375
- | `--tables` | Comma-separated list of tables to generate (supports `parent.child` notation) |
376
- | `--env` | Path to .env file to load |
377
-
378
- Auto-detection:
379
-
380
- - Columns with `DEFAULT` values are marked as optional (not `required`)
381
- - Timestamp columns (`created_at`, `updated_at`, `modified_at`, `createdAt`, etc.) are excluded from the model structure and added to the option object
382
- - Soft-delete columns (`is_deleted`, `deleted`, `is_active`, `archived`, etc.) are excluded from the structure and set as `safeDelete` in the option
383
- - Multi-column unique indexes are correctly grouped for the unique constraint parameter
384
-
385
- Generated output:
386
-
387
- ```
388
- models/
389
- users.js # model(db, "users", {...}, "user_id", ["email"], { safeDelete: "is_deleted", ... })
390
- posts.js # model(db, "posts", {...}, "post_id", ["post_id"])
391
- index.js # exports { users, posts }
392
- ```
393
-
394
- ### generate-route
395
-
396
- Generates Express route files for each model. If models don't exist yet, it auto-generates them first. Also generates an OpenAPI 3.0 spec (`openapi.json`) from the model metadata. This is called automatically by `generate-app`, but can be used standalone.
397
-
398
- ```bash
399
- # From existing models
400
- db-model-router-generate-route --models ./models --output ./routes
401
-
402
- # Auto-generate models + routes in one step
403
- db-model-router-generate-route --type mysql --env .env --models ./models --output ./routes
404
-
405
- # SQLite3 one-liner
406
- db-model-router-generate-route --type sqlite3 --database ./myapp.db
407
- ```
408
-
409
- Options:
410
-
411
- | Option | Description |
412
- | ------------ | -------------------------------------------------------------------------- |
413
- | `--models` | Path to models directory (default: ./models) |
414
- | `--output` | Output directory for routes (default: ./routes) |
415
- | `--type` | Database type — triggers model generation if models are missing |
416
- | `--host` | Database host (passed to model generation) |
417
- | `--port` | Database port (passed to model generation) |
418
- | `--database` | Database name or file path (passed to model generation) |
419
- | `--user` | Database user (passed to model generation) |
420
- | `--password` | Database password (passed to model generation) |
421
- | `--schema` | Schema name (passed to model generation) |
422
- | `--tables` | Comma-separated tables, supports `parent.child` notation for relationships |
423
- | `--env` | Path to .env file (passed to model generation) |
424
-
425
- #### Parent-Child Relationships (Foreign Keys)
426
-
427
- Use dot notation in `--tables` to declare parent-child relationships. This works in `generate-route`, `generate-app`, and `generate-model`. The generator creates nested routes that automatically scope child queries by the parent's foreign key.
428
-
429
- ```bash
430
- # Declare that comments belong to posts
431
- db-model-router-generate-route --type mysql --env .env --tables users,posts,posts.comments
432
-
433
- # Same via generate-app
434
- db-model-router-generate-app --type mysql --env .env --tables users,posts,posts.comments
435
- ```
436
-
437
- The FK column is derived by convention: `<parent_singular>_id` (e.g., `posts.comments` → `post_id`).
438
-
439
- This generates:
440
-
441
- ```
442
- routes/
443
- users.js # route(users)
444
- posts.js # route(posts)
445
- comments.js # route(comments) — direct access
446
- comments_child_of_posts.js # route(comments, { post_id: "params.post_id" }) — scoped
447
- index.js # mounts all routes
448
- openapi.json # OpenAPI 3.0 spec
449
- ```
450
-
451
- The generated `index.js` mounts both nested and top-level routes:
452
-
453
- ```js
454
- // Nested: GET /api/posts/:post_id/comments — returns only comments for that post
455
- router.use("/posts/:post_id/comments", commentsChildRoute);
456
-
457
- // Direct: GET /api/comments — returns all comments
458
- router.use("/comments", commentsRoute);
459
- ```
460
-
461
- The child route file uses payload override to scope every query:
462
-
463
- ```js
464
- // comments_child_of_posts.js
465
- module.exports = route(comments, { post_id: "params.post_id" });
466
- ```
467
-
468
- #### Generated output (without relationships)
469
-
470
- ```
471
- routes/
472
- users.js # route(users)
473
- posts.js # route(posts)
474
- index.js # express.Router() mounting all routes at /users, /posts, etc.
475
- openapi.json # OpenAPI 3.0 spec
476
- ```
477
-
478
429
  ## Environment Setup (Docker)
479
430
 
480
431
  A `docker-compose.yml` is included for running all supported databases locally:
package/docs/SKILL.md CHANGED
@@ -1,15 +1,45 @@
1
+ ---
2
+ name: db-model-router
3
+ description: Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API. Works with express or ultimate-express.
4
+ ---
5
+
1
6
  # db-model-router — LLM Skill Reference
2
7
 
3
- Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API.
8
+ Database-agnostic REST API generator for Node.js/Express. Define model → get CRUD API + Express routes. 9 adapters, identical API. Works with `express` or `ultimate-express`.
9
+
10
+ ## LLM Workflow (follow this order)
11
+
12
+ 1. **Scaffold**: `db-model-router-init --framework express --database postgres --session redis --rateLimiting --helmet --logger` (all flags → zero prompts)
13
+ 2. **Migrations**: Write SQL/JS migration files into `migrations/` for the user's schema, then `npm run migrate`
14
+ 3. **Generate models**: `db-model-router-generate-model --type postgres --env .env --output ./models`
15
+ 4. **Generate routes + tests**: `db-model-router-generate-route --models ./models --output ./routes --tables users,posts,posts.comments`
16
+ 5. **Run**: `npm run dev`
17
+
18
+ Step 1 creates the project. Step 2 defines the schema. Steps 3-4 introspect the DB and generate models, routes, tests, and OpenAPI spec. Step 5 starts the server.
4
19
 
5
20
  ## Install
6
21
 
7
22
  ```bash
8
- npm install db-model-router <driver>
23
+ npm install db-model-router <framework> <driver>
9
24
  ```
10
25
 
26
+ Frameworks: `express` or `ultimate-express` (auto-detected, prefers ultimate-express)
11
27
  Drivers: `mysql2`, `pg`, `better-sqlite3`, `mongodb`, `mssql`, `oracledb`, `ioredis`, `@aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb`
12
28
 
29
+ ## Adapters
30
+
31
+ | Module Key | Driver | Default Port |
32
+ | ------------- | ------------------------------------------------ | ------------ |
33
+ | `mysql` | mysql2 | 3306 |
34
+ | `postgres` | pg | 5432 |
35
+ | `sqlite3` | better-sqlite3 | — |
36
+ | `mongodb` | mongodb | 27017 |
37
+ | `mssql` | mssql | 1433 |
38
+ | `cockroachdb` | pg | 26257 |
39
+ | `oracle` | oracledb | 1521 |
40
+ | `redis` | ioredis | 6379 |
41
+ | `dynamodb` | @aws-sdk/client-dynamodb + @aws-sdk/lib-dynamodb | — |
42
+
13
43
  ## Init → Connect → Model → Route
14
44
 
15
45
  ```js
@@ -42,12 +72,12 @@ app.use("/users", route(users));
42
72
 
43
73
  ## model(db, table, structure, pk, unique, option)
44
74
 
45
- | Param | Type | Description |
46
- | --------- | --------------- | --------------------------------------------------------------------------------------------------------------------- |
47
- | structure | `{col: "rule"}` | Types: `string\|integer\|numeric\|object`. Prefix `required\|` for NOT NULL. Exclude PK, timestamps, soft-delete cols |
48
- | pk | string | Primary key column. Default `"id"` |
49
- | unique | string[] | Columns for upsert conflict resolution |
50
- | option | object | `{ safeDelete, created_at, modified_at }` — column names or null |
75
+ | Param | Type | Description |
76
+ | --------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
77
+ | structure | `{col: "rule"}` | Types: `string\|integer\|numeric\|boolean\|object`. Prefix `required\|` for NOT NULL. Exclude PK, timestamps, soft-delete cols |
78
+ | pk | string | Primary key column. Default `"id"` |
79
+ | unique | string[] | Columns for upsert conflict resolution |
80
+ | option | object | `{ safeDelete, created_at, modified_at }` — column names or null |
51
81
 
52
82
  ## Model Methods (all async)
53
83
 
@@ -83,14 +113,22 @@ Structure: `[OR_groups[AND_conditions[col, op, val]]]`
83
113
  Ops: `= != < > <= >= like not like in not in`
84
114
 
85
115
  ```js
116
+ // AND: age > 25 AND type = 1
86
117
  [
87
118
  [
88
119
  ["age", ">", 25],
89
120
  ["type", "=", 1],
90
121
  ],
91
- ][([["name", "=", "A"]], [["name", "=", "B"]])][[["type", "in", [1, 2, 3]]]][ // AND // OR // IN
122
+ ][
123
+ // OR: name = "A" OR name = "B"
124
+ ([["name", "=", "A"]], [["name", "=", "B"]])
125
+ ][
126
+ // IN
127
+ [["type", "in", [1, 2, 3]]]
128
+ ][
129
+ // LIKE (auto-wraps with %)
92
130
  [["name", "like", "Ali"]]
93
- ]; // LIKE %Ali%
131
+ ];
94
132
  ```
95
133
 
96
134
  ## route(model, override?)
@@ -100,14 +138,14 @@ Generates Express Router with 9 endpoints:
100
138
  | Method | Path | Action |
101
139
  | ------ | ------ | -------------------------------- |
102
140
  | GET | `/:pk` | Get by PK |
103
- | POST | `/:id` | Insert single |
104
- | PUT | `/:id` | Update single (PK from URL) |
105
- | PATCH | `/:id` | Partial update |
106
- | DELETE | `/:id` | Delete single |
141
+ | POST | `/add` | Insert single |
142
+ | PUT | `/:pk` | Update single (PK from URL) |
143
+ | PATCH | `/:pk` | Partial update (PK from URL) |
144
+ | DELETE | `/:pk` | Delete single |
107
145
  | GET | `/` | List (page, size, sort, filters) |
108
146
  | POST | `/` | Bulk insert `{data:[...]}` |
109
147
  | PUT | `/` | Bulk update `{data:[...]}` |
110
- | DELETE | `/` | Bulk delete `{data:[...]}` |
148
+ | DELETE | `/` | Bulk delete |
111
149
 
112
150
  **Payload override** (multi-tenancy): `route(m, { tenant_id: "user.tenant_id" })` — maps cols to `req` paths via lodash.get.
113
151
 
@@ -115,13 +153,65 @@ Generates Express Router with 9 endpoints:
115
153
 
116
154
  ## CLI Tools
117
155
 
118
- ### generate-app (full scaffold)
156
+ ### db-model-router-init (interactive project scaffold)
157
+
158
+ Scaffolds a complete Express-based REST API project from scratch. Supports both interactive prompts and fully non-interactive CLI flags.
119
159
 
120
160
  ```bash
121
- db-model-router-generate-app --type mysql --env .env [--output ./dir] [--tables users,posts,posts.comments]
161
+ # Interactive (prompts for everything)
162
+ npx db-model-router-init
163
+
164
+ # Fully non-interactive (LLM-friendly — no prompts)
165
+ db-model-router-init --framework express --database postgres --session redis --rateLimiting --helmet --logger
166
+
167
+ # Partial flags — only prompts for missing values
168
+ db-model-router-init --database mysql --session memory
122
169
  ```
123
170
 
124
- Creates: `app.js`, `models/`, `routes/`, `middleware/logger.js`, `.env.example`, `.gitignore`, `migrations/`, `sessions/`, `openapi.json`
171
+ Flags: `--framework <name>`, `--database <name>` (or `--db`), `--session <type>`, `--rateLimiting`, `--helmet`, `--logger`, `--help`
172
+
173
+ When all 6 options are provided, runs with zero prompts.
174
+
175
+ Prompts (for missing values): framework (`ultimate-express`|`express`), database (9 options), session (`memory`|`redis`|`database`), rate limiting (y/n), helmet (y/n), logger (y/n).
176
+
177
+ If no `package.json` exists, runs `npm init` first. After prompts: generates files, updates `package.json`, runs `npm install`.
178
+
179
+ Generated structure:
180
+
181
+ ```
182
+ app.js, .env, .env.example, .gitignore, migrate.js, add_migration.js,
183
+ middleware/logger.js, migrations/{timestamp}_create_migrations_table.sql|.js
184
+ ```
185
+
186
+ Session migration (`{timestamp}_create_sessions_table.sql`) generated only for SQL databases with `session=database`.
187
+
188
+ Scripts added: `start` (node app.js), `dev` (nodemon app.js), `test`, `migrate` (node migrate.js), `add_migration` (node add_migration.js).
189
+
190
+ Dependencies always included: `db-model-router`, `dotenv`, selected framework, database driver(s), `express-session`. DevDeps: `nodemon`.
191
+ Conditional: `connect-redis` + `ioredis` (session=redis), `express-rate-limit`, `helmet`, `express-mung` (logger).
192
+
193
+ #### Environment Variables by Database
194
+
195
+ | Database | Variables |
196
+ | ----------- | --------------------------------------------------------------------------- |
197
+ | mysql | `PORT=3000 DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
198
+ | postgres | `PORT=3000 DB_HOST DB_PORT=5432 DB_NAME DB_USER DB_PASS` |
199
+ | cockroachdb | `PORT=3000 DB_HOST DB_PORT=26257 DB_NAME DB_USER DB_PASS` |
200
+ | sqlite3 | `PORT=3000 DB_NAME=./data.db` |
201
+ | mongodb | `PORT=3000 DB_HOST DB_PORT=27017 DB_NAME DB_USER DB_PASS` |
202
+ | mssql | `PORT=3000 DB_HOST DB_PORT=1433 DB_NAME DB_USER DB_PASS` |
203
+ | oracle | `PORT=3000 DB_HOST DB_PORT=1521 DB_NAME DB_USER DB_PASS` |
204
+ | redis | `PORT=3000 DB_HOST DB_PORT=6379 DB_PASS` |
205
+ | dynamodb | `PORT=3000 AWS_REGION AWS_ENDPOINT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY` |
206
+
207
+ When `session=redis` and database ≠ redis: adds `REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASS`.
208
+
209
+ #### Migration Infrastructure
210
+
211
+ - `migrate.js` — reads `migrations/` dir, diffs against `_migrations` tracking table, executes pending in order
212
+ - `add_migration.js` — creates timestamped empty migration (`.sql` for SQL, `.js` for NoSQL)
213
+ - Tracking table `_migrations`: `{id, filename, executed_at, checksum}` (SQL) or equivalent collection/hash/table (NoSQL)
214
+ - Timestamp format: `YYYYMMDDHHMMSS`
125
215
 
126
216
  ### generate-model (DB introspection → model files)
127
217
 
@@ -129,15 +219,28 @@ Creates: `app.js`, `models/`, `routes/`, `middleware/logger.js`, `.env.example`,
129
219
  db-model-router-generate-model --type <db> --env .env [--output ./models] [--tables t1,t2] [--schema public]
130
220
  ```
131
221
 
132
- Auto-detects: PK, unique indexes, DEFAULT→optional, timestamp cols, soft-delete cols.
222
+ Options: `--type`, `--host`, `--port`, `--database`, `--user`, `--password`, `--schema`, `--output`, `--tables`, `--env`
223
+
224
+ Auto-detects: PK, unique indexes, DEFAULT→optional, timestamp cols (`created_at`, `updated_at`, `modified_at`, `createdAt`, etc.), soft-delete cols (`is_deleted`, `deleted`, `is_active`, `archived`, etc.). Multi-column unique indexes correctly grouped.
133
225
 
134
- ### generate-route (model files → route files + OpenAPI)
226
+ ### generate-route (model files → route files + tests + OpenAPI)
135
227
 
136
228
  ```bash
137
229
  db-model-router-generate-route --models ./models --output ./routes [--tables posts,posts.comments]
138
230
  ```
139
231
 
140
- Dot notation `parent.child` creates nested routes: `parent/:parent_id/child` with FK scoping via `<parent_singular>_id`.
232
+ Options: `--models`, `--output`, `--type`, `--host`, `--port`, `--database`, `--user`, `--password`, `--schema`, `--tables`, `--env`
233
+
234
+ Generates:
235
+
236
+ - Route files for each model
237
+ - `index.js` mounting all routes on an Express Router
238
+ - `openapi.json` (OpenAPI 3.0 spec)
239
+ - Test files in `tests/` directory for all routes and methods (uses `supertest` + `assert`)
240
+
241
+ Dot notation `parent.child` creates nested routes: `parent/:parent_id/child` with FK scoping via `<parent_singular>_id`. Also generates child route test files.
242
+
243
+ Generated test files cover all 8 CRUD endpoints per table: GET by ID, POST add, PUT update, DELETE, list, bulk insert, bulk update, bulk delete.
141
244
 
142
245
  ## Connection Configs
143
246
 
@@ -192,7 +295,7 @@ db.connect({ region, endpoint, accessKeyId, secretAccessKey });
192
295
  ## Rules
193
296
 
194
297
  1. `init()` before `db.connect()`. Don't destructure `db` before `init()`.
195
- 2. `modelStructure` excludes: PK col, timestamp cols, soft-delete cols.
298
+ 2. `model structure` excludes: PK col, timestamp cols, soft-delete cols.
196
299
  3. `update()`/`patch()` require PK in payload. `upsert()` PK is optional.
197
300
  4. `findOne()` returns `false` on no match. `byId()` returns `null`.
198
301
  5. Bulk ops wrap in `{ data: [...] }`. Single ops use flat object.
@@ -200,3 +303,72 @@ db.connect({ region, endpoint, accessKeyId, secretAccessKey });
200
303
  7. `safeDelete` makes `remove()` soft-delete; all reads auto-filter deleted rows.
201
304
  8. `list()` defaults: page=0, size=30. `sort` array: `["-col"]` for DESC.
202
305
  9. CommonJS only (`require`). Use dynamic `import()` for ESM.
306
+ 10. `db-model-router-init` for new projects (interactive). `generate-model` + `generate-route` for existing databases. Or use the unified `db-model-router` CLI with a `dbmr.schema.json` file.
307
+ 11. `generate-route` auto-generates test files alongside routes. Tests use `supertest`.
308
+
309
+ ## Schema-Driven Workflow (Unified CLI)
310
+
311
+ The `db-model-router` command is the unified CLI entry point. It uses a single `dbmr.schema.json` file as the source of truth for all code generation.
312
+
313
+ ### LLM Workflow (schema-driven)
314
+
315
+ 1. **Inspect** (existing DB): `db-model-router inspect --type postgres --env .env` → writes `dbmr.schema.json`
316
+ 2. **Edit** `dbmr.schema.json` — add relationships, tweak columns, set options
317
+ 3. **Generate**: `db-model-router generate --from dbmr.schema.json` → models, routes, tests, OpenAPI
318
+ 4. **Doctor**: `db-model-router doctor --from dbmr.schema.json` → validate schema + check sync
319
+ 5. **Run**: `npm run dev`
320
+
321
+ Or for new projects: `db-model-router init --from dbmr.schema.json --yes`
322
+
323
+ ### Subcommands
324
+
325
+ | Subcommand | Description |
326
+ | ---------- | ----------------------------------------------------------- |
327
+ | `init` | Scaffold project (optionally from schema file via `--from`) |
328
+ | `inspect` | Introspect live DB → produce `dbmr.schema.json` |
329
+ | `generate` | Generate models/routes/tests/OpenAPI from schema |
330
+ | `doctor` | Validate schema, check deps, verify files in sync |
331
+ | `diff` | Preview changes regeneration would make (read-only) |
332
+
333
+ ### Universal Flags
334
+
335
+ All subcommands accept: `--yes`, `--json`, `--dry-run`, `--no-install`, `--help`.
336
+
337
+ - `--yes`: suppress prompts, accept defaults
338
+ - `--json`: machine-readable JSON output only
339
+ - `--dry-run`: preview without writing files
340
+ - `--no-install`: skip `npm install`
341
+
342
+ ### dbmr.schema.json Format
343
+
344
+ ```json
345
+ {
346
+ "adapter": "postgres",
347
+ "framework": "express",
348
+ "options": {
349
+ "session": "redis",
350
+ "rateLimiting": true,
351
+ "helmet": true,
352
+ "logger": true
353
+ },
354
+ "tables": {
355
+ "users": {
356
+ "columns": {
357
+ "name": "required|string",
358
+ "email": "required|string",
359
+ "age": "integer"
360
+ },
361
+ "pk": "id",
362
+ "unique": ["email"],
363
+ "softDelete": "is_deleted",
364
+ "timestamps": { "created_at": "created_at", "modified_at": "updated_at" }
365
+ }
366
+ },
367
+ "relationships": [
368
+ { "parent": "users", "child": "posts", "foreignKey": "user_id" }
369
+ ]
370
+ }
371
+ ```
372
+
373
+ Fields per table: `columns` (required), `pk` (default `"id"`), `unique` (default `[pk]`), `softDelete`, `timestamps`.
374
+ Column rules: `(required|)?(string|integer|numeric|boolean|object)`.