db-model-router 1.0.4 → 1.0.6
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 +110 -16
- package/TODO.md +15 -0
- package/dbmr.schema.json +333 -0
- package/docker-compose.yml +1 -1
- package/package.json +8 -7
- package/scripts/demo-create.js +47 -0
- package/skill/SKILL.md +464 -0
- package/skill/references/cockroachdb.md +49 -0
- package/skill/references/dynamodb.md +53 -0
- package/skill/references/mongodb.md +56 -0
- package/skill/references/mssql.md +55 -0
- package/skill/references/oracle.md +52 -0
- package/skill/references/postgres.md +50 -0
- package/skill/references/redis.md +53 -0
- package/skill/references/sqlite3.md +43 -0
- package/src/cli/commands/generate.js +95 -31
- package/src/cli/commands/help.js +12 -7
- package/src/cli/commands/init.js +2 -2
- package/src/cli/commands/inspect.js +1 -0
- package/src/cli/diff-engine.js +54 -23
- package/src/cli/generate-db-manager.js +1573 -0
- package/src/cli/generate-docs-route.js +31 -0
- package/src/cli/generate-migration.js +356 -0
- package/src/cli/generate-model.js +9 -4
- package/src/cli/generate-openapi.js +40 -13
- package/src/cli/generate-route.js +55 -27
- package/src/cli/init/dependencies.js +3 -0
- package/src/cli/init/generators.js +37 -31
- package/src/cli/init.js +8 -8
- package/src/cli/main.js +2 -2
- package/src/cockroachdb/db.js +90 -59
- package/src/commons/route.js +20 -20
- package/src/commons/validator.js +58 -1
- package/src/dynamodb/db.js +50 -27
- package/src/mongodb/db.js +1 -0
- package/src/mssql/db.js +89 -61
- package/src/mysql/db.js +1 -0
- package/src/oracle/db.js +1 -0
- package/src/postgres/db.js +61 -41
- package/src/redis/db.js +1 -0
- package/src/schema/schema-parser.js +43 -1
- package/src/schema/schema-printer.js +7 -0
- package/src/schema/schema-validator.js +17 -0
- package/src/sqlite3/db.js +12 -0
- package/docs/SKILL.md +0 -419
- package/src/cli/commands/generate-llm-docs.js +0 -418
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: db-model-router
|
|
3
|
+
description: Use this skill whenever the user wants to build a REST API with Node.js/Express backed by any database — including MySQL, PostgreSQL, SQLite, MongoDB, MSSQL, Oracle, Redis, DynamoDB, or CockroachDB. Trigger on any mention of db-model-router, or when the user asks to scaffold, generate, or wire up a CRUD API, models, or routes for a Node/Express backend. Also trigger when the user asks about connecting to a specific database with db-model-router, model definitions, filter syntax, bulk operations, schema-driven generation, or CLI commands like init/generate/inspect/doctor/diff.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# db-model-router — LLM Skill Reference
|
|
7
|
+
|
|
8
|
+
Database-agnostic REST API generator for Node.js/Express. Define a model → get full CRUD + Express routes. 10 adapters, identical API. Generated projects use ESM (`import`/`export`).
|
|
9
|
+
|
|
10
|
+
## Adapter Reference Files
|
|
11
|
+
|
|
12
|
+
For adapter-specific connection options, env vars, upsert behavior, and table creation SQL, read the relevant file **on demand** (only when the user's task involves that adapter):
|
|
13
|
+
|
|
14
|
+
| Adapter | Reference File |
|
|
15
|
+
| ------------- | --------------------------- |
|
|
16
|
+
| `postgres` | `references/postgres.md` |
|
|
17
|
+
| `cockroachdb` | `references/cockroachdb.md` |
|
|
18
|
+
| `sqlite3` | `references/sqlite3.md` |
|
|
19
|
+
| `mongodb` | `references/mongodb.md` |
|
|
20
|
+
| `mssql` | `references/mssql.md` |
|
|
21
|
+
| `oracle` | `references/oracle.md` |
|
|
22
|
+
| `redis` | `references/redis.md` |
|
|
23
|
+
| `dynamodb` | `references/dynamodb.md` |
|
|
24
|
+
|
|
25
|
+
MySQL/MariaDB use `mysql2` — no separate reference file needed (see Connection Configs below).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## LLM Workflow (follow this order for new projects)
|
|
30
|
+
|
|
31
|
+
1. **Scaffold**: `db-model-router init --framework express --database postgres --session redis --rateLimiting --helmet --logger --yes`
|
|
32
|
+
2. **Start infra**: `npm run docker:up`
|
|
33
|
+
3. **Migrations**: Write SQL/JS files into `migrations/`, then `npm run migrate`
|
|
34
|
+
4. **Generate models**: `db-model-router generate --from dbmr.schema.json --models`
|
|
35
|
+
5. **Generate routes + tests**: `db-model-router generate --from dbmr.schema.json --routes --tests`
|
|
36
|
+
6. **Run**: `npm run dev`
|
|
37
|
+
|
|
38
|
+
For existing databases, use `inspect` first:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
db-model-router inspect --type postgres --env .env # → writes dbmr.schema.json
|
|
42
|
+
db-model-router generate --from dbmr.schema.json # → models, routes, tests, OpenAPI
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install db-model-router <framework> <driver>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Frameworks: `express` or `ultimate-express` (auto-detected; prefers ultimate-express when both present).
|
|
54
|
+
|
|
55
|
+
| Adapter | Driver | Install |
|
|
56
|
+
| ------------- | ------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
57
|
+
| `mysql` | mysql2 | `npm i db-model-router mysql2` |
|
|
58
|
+
| `mariadb` | mysql2 | `npm i db-model-router mysql2` |
|
|
59
|
+
| `postgres` | pg | `npm i db-model-router pg` |
|
|
60
|
+
| `sqlite3` | better-sqlite3 | `npm i db-model-router better-sqlite3` |
|
|
61
|
+
| `mongodb` | mongodb | `npm i db-model-router mongodb` |
|
|
62
|
+
| `mssql` | mssql | `npm i db-model-router mssql` |
|
|
63
|
+
| `cockroachdb` | pg | `npm i db-model-router pg` |
|
|
64
|
+
| `oracle` | oracledb | `npm i db-model-router oracledb` |
|
|
65
|
+
| `redis` | ioredis | `npm i db-model-router ioredis` |
|
|
66
|
+
| `dynamodb` | @aws-sdk/client-dynamodb + @aws-sdk/lib-dynamodb | `npm i db-model-router @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb` |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Init → Connect → Model → Route
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// CJS
|
|
74
|
+
const { init, db, model, route } = require("db-model-router");
|
|
75
|
+
|
|
76
|
+
// ESM (generated projects)
|
|
77
|
+
import dbModelRouter from "db-model-router";
|
|
78
|
+
const { init, db, model, route } = dbModelRouter;
|
|
79
|
+
|
|
80
|
+
init("postgres"); // call BEFORE db.connect() — do NOT destructure db before init()
|
|
81
|
+
db.connect({ host, port: 5432, user, password, database });
|
|
82
|
+
|
|
83
|
+
const users = model(
|
|
84
|
+
db,
|
|
85
|
+
"users",
|
|
86
|
+
{
|
|
87
|
+
name: "required|string",
|
|
88
|
+
email: "required|string",
|
|
89
|
+
age: "integer",
|
|
90
|
+
meta: "object",
|
|
91
|
+
},
|
|
92
|
+
"id", // primary key column
|
|
93
|
+
["email"], // unique columns (for upsert conflict)
|
|
94
|
+
{
|
|
95
|
+
safeDelete: "is_deleted", // soft-delete column
|
|
96
|
+
created_at: "created_at", // auto-managed timestamp
|
|
97
|
+
modified_at: "updated_at", // auto-managed timestamp
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
app.use("/users", route(users));
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> **Critical**: Call `init()` before `db.connect()`. Default adapter is `mysql`. Do NOT destructure `db` before `init()` — it is a getter.
|
|
105
|
+
> **ESM note**: The library is CJS. Generated ESM projects must use default import (above) — NOT named `import { init }`.
|
|
106
|
+
|
|
107
|
+
For adapter-specific connect options (ports, env vars, upsert behavior), read the relevant file from `references/`.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## model(db, table, structure, pk, unique, option)
|
|
112
|
+
|
|
113
|
+
| Param | Type | Description |
|
|
114
|
+
| ----------- | --------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| `structure` | `{col: "rule"}` | Types: `string\|integer\|numeric\|boolean\|object\|datetime\|auto_increment`. Prefix `required\|` for NOT NULL. |
|
|
116
|
+
| `pk` | string | Primary key column. Convention: `<table>_id` |
|
|
117
|
+
| `unique` | string[] | Columns for upsert conflict resolution |
|
|
118
|
+
| `option` | object | `{ safeDelete, created_at, modified_at }` — column names or null |
|
|
119
|
+
|
|
120
|
+
> PK, timestamp, soft-delete, and `auto_increment` cols are auto-excluded from insert/update payloads.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Model Methods (all async)
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
// INSERT
|
|
128
|
+
await m.insert({ name: "Alice", email: "a@b.com", age: 30 }) // → {id:1, name:"Alice", ...}
|
|
129
|
+
await m.insert({ data: [{...}, {...}] }) // → {rows:2, message, type:"success"}
|
|
130
|
+
|
|
131
|
+
// UPDATE — PK required in payload
|
|
132
|
+
await m.update({ id: 1, name: "Alice V2", email: "a@b.com", age: 31 })
|
|
133
|
+
await m.update({ data: [{id:1,...}, {id:2,...}] })
|
|
134
|
+
|
|
135
|
+
// PATCH — partial update, only sends changed fields, PK required
|
|
136
|
+
await m.patch({ id: 1, age: 35 }) // → full merged record
|
|
137
|
+
|
|
138
|
+
// UPSERT — PK optional, uses unique cols for conflict
|
|
139
|
+
await m.upsert({ email: "new@b.com", name: "New", age: 20 })
|
|
140
|
+
|
|
141
|
+
// READ
|
|
142
|
+
await m.byId(1) // → record or null
|
|
143
|
+
await m.find({ name: "Alice" }) // → {data:[], count}
|
|
144
|
+
await m.findOne({ email: "a@b.com" }) // → record or false
|
|
145
|
+
await m.list({ page: 0, size: 10, sort: ["-age"] }) // → {data:[], count}
|
|
146
|
+
|
|
147
|
+
// DELETE — safeDelete: sets column=1; without: hard delete
|
|
148
|
+
await m.remove(1)
|
|
149
|
+
await m.remove({ name: "Bob" })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Filter Syntax
|
|
155
|
+
|
|
156
|
+
Structure: `[OR_groups[AND_conditions[col, op, val]]]`
|
|
157
|
+
Operators: `= != < > <= >= LIKE NOT LIKE IN NOT IN`
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
// AND: age > 25 AND type = 1
|
|
161
|
+
[
|
|
162
|
+
[
|
|
163
|
+
["age", ">", 25],
|
|
164
|
+
["type", "=", 1],
|
|
165
|
+
],
|
|
166
|
+
][
|
|
167
|
+
// OR: name = "A" OR name = "B"
|
|
168
|
+
([["name", "=", "A"]], [["name", "=", "B"]])
|
|
169
|
+
][
|
|
170
|
+
// IN
|
|
171
|
+
[["type", "in", [1, 2, 3]]]
|
|
172
|
+
][
|
|
173
|
+
// LIKE (auto-wraps with %)
|
|
174
|
+
[["name", "like", "Ali"]]
|
|
175
|
+
];
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Query Parameter Filter Operators
|
|
179
|
+
|
|
180
|
+
When using `GET /` (list endpoint), query parameters are automatically parsed into filter conditions. Special value prefixes and patterns control the operator:
|
|
181
|
+
|
|
182
|
+
| Query Param Value | Operator | Example | Resulting Filter |
|
|
183
|
+
| ------------------ | ---------- | ----------------------------- | ------------------------------------ |
|
|
184
|
+
| `value` | `=` | `?name=john` | `name = 'john'` |
|
|
185
|
+
| `!value` | `!=` | `?name=!john` | `name != 'john'` |
|
|
186
|
+
| `>value` | `>` | `?age=>25` | `age > 25` |
|
|
187
|
+
| `>=value` (`>%3D`) | `>=` | `?age=>%3D25` | `age >= 25` |
|
|
188
|
+
| `<value` | `<` | `?age=<25` | `age < 25` |
|
|
189
|
+
| `<=value` (`<%3D`) | `<=` | `?age=<%3D25` | `age <= 25` |
|
|
190
|
+
| `%value%` (`%25`) | `LIKE` | `?name=%25john%25` | `name LIKE '%john%'` |
|
|
191
|
+
| `!%value%` | `NOT LIKE` | `?name=!%25john%25` | `name NOT LIKE '%john%'` |
|
|
192
|
+
| `in(a,b,c)` | `IN` | `?status=in(active,pending)` | `status IN ('active','pending')` |
|
|
193
|
+
| `!in(a,b,c)` | `NOT IN` | `?status=!in(active,pending)` | `status NOT IN ('active','pending')` |
|
|
194
|
+
|
|
195
|
+
`%` is URL-encoded as `%25`; `=` in `>=`/`<=` is URL-encoded as `%3D`. `LIKE` patterns follow SQL conventions: `%25john%25` → contains, `%25john` → ends with, `john%25` → starts with. `IN`/`NOT IN` values are comma-separated inside parentheses.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## route(model, override?)
|
|
200
|
+
|
|
201
|
+
Generates an Express Router with 9 endpoints:
|
|
202
|
+
|
|
203
|
+
| Method | Path | Action |
|
|
204
|
+
| ------ | ------ | -------------------------------- |
|
|
205
|
+
| GET | `/:pk` | Get by PK |
|
|
206
|
+
| POST | `/add` | Insert single |
|
|
207
|
+
| PUT | `/:pk` | Update single (PK from URL) |
|
|
208
|
+
| PATCH | `/:pk` | Partial update (PK from URL) |
|
|
209
|
+
| DELETE | `/:pk` | Delete single |
|
|
210
|
+
| GET | `/` | List (page, size, sort, filters) |
|
|
211
|
+
| POST | `/` | Bulk insert `{data:[...]}` |
|
|
212
|
+
| PUT | `/` | Bulk update `{data:[...]}` |
|
|
213
|
+
| DELETE | `/` | Bulk delete `{filter_object}` |
|
|
214
|
+
|
|
215
|
+
**Payload override** (multi-tenancy): `route(m, { tenant_id: "user.tenant_id" })` — maps columns to `req` paths via lodash.get.
|
|
216
|
+
|
|
217
|
+
**Query params**: `select_columns=name,email`, `output_content_type=csv|xml|json`, `sort=-age,name`
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## CLI Reference
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
db-model-router <command> [options]
|
|
225
|
+
db-model-router help <command>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `init` — Scaffold project
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Fully non-interactive (LLM-friendly)
|
|
232
|
+
db-model-router init --framework express --database postgres --session redis \
|
|
233
|
+
--rateLimiting --helmet --logger --yes
|
|
234
|
+
|
|
235
|
+
# With Loki/Grafana logging
|
|
236
|
+
db-model-router init --database postgres --logger --loki --yes
|
|
237
|
+
|
|
238
|
+
# From schema file
|
|
239
|
+
db-model-router init --from dbmr.schema.json --yes --no-install
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Key flags: `--framework`, `--database` (or `--db`), `--session`, `--output`, `--rateLimiting`, `--helmet`, `--logger`, `--loki`, `--yes`, `--no-install`
|
|
243
|
+
|
|
244
|
+
Generated structure (ESM, `"type":"module"`):
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
app.js Express entry point
|
|
248
|
+
.env / .env.example Env config (random passwords)
|
|
249
|
+
docker-compose.yml DB + CloudBeaver + optional Loki/Grafana
|
|
250
|
+
<output>/commons/db.js Database init + global.db
|
|
251
|
+
<output>/commons/migrate.js Migration runner
|
|
252
|
+
<output>/route/index.js Central route mounting
|
|
253
|
+
<output>/route/health.js GET /health endpoint
|
|
254
|
+
<output>/migrations/ Initial migration files
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Docker services auto-generated: database, Redis (if session=redis), CloudBeaver (SQL/MongoDB, port 8978), Loki + Grafana (if --loki).
|
|
258
|
+
|
|
259
|
+
Scripts: `start`, `dev`, `test`, `migrate`, `add_migration`, `docker:build`, `docker:up`, `docker:down`.
|
|
260
|
+
|
|
261
|
+
### `inspect` — Introspect existing DB → schema
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
db-model-router inspect --type postgres --env .env [--out schema.json] [--tables t1,t2]
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### `generate` — Generate code from schema
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
db-model-router generate --from dbmr.schema.json [--models] [--routes] [--openapi] [--tests] [--llm-docs]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
No flags = generate all.
|
|
274
|
+
|
|
275
|
+
### `doctor` — Validate schema + check file sync
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
db-model-router doctor [--from dbmr.schema.json] [--json]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `diff` — Preview changes without writing
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
db-model-router diff [--from dbmr.schema.json] [--json]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Universal flags (all commands): `--yes`, `--json`, `--dry-run`, `--no-install`, `--help`
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Schema-Driven Workflow (dbmr.schema.json)
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"adapter": "postgres",
|
|
296
|
+
"framework": "express",
|
|
297
|
+
"options": {
|
|
298
|
+
"session": "redis",
|
|
299
|
+
"rateLimiting": true,
|
|
300
|
+
"helmet": true,
|
|
301
|
+
"logger": true
|
|
302
|
+
},
|
|
303
|
+
"tables": {
|
|
304
|
+
"users": {
|
|
305
|
+
"columns": {
|
|
306
|
+
"user_id": "auto_increment",
|
|
307
|
+
"name": "required|string",
|
|
308
|
+
"email": "required|string",
|
|
309
|
+
"is_deleted": "boolean",
|
|
310
|
+
"created_at": "datetime",
|
|
311
|
+
"updated_at": "datetime"
|
|
312
|
+
},
|
|
313
|
+
"pk": "user_id",
|
|
314
|
+
"unique": ["email"],
|
|
315
|
+
"softDelete": "is_deleted",
|
|
316
|
+
"timestamps": { "created_at": "created_at", "modified_at": "updated_at" },
|
|
317
|
+
"parent": null
|
|
318
|
+
},
|
|
319
|
+
"posts": {
|
|
320
|
+
"columns": {
|
|
321
|
+
"post_id": "auto_increment",
|
|
322
|
+
"title": "required|string",
|
|
323
|
+
"user_id": "required|integer",
|
|
324
|
+
"created_at": "datetime"
|
|
325
|
+
},
|
|
326
|
+
"pk": "post_id",
|
|
327
|
+
"unique": ["post_id"],
|
|
328
|
+
"parent": null
|
|
329
|
+
},
|
|
330
|
+
"comments": {
|
|
331
|
+
"columns": {
|
|
332
|
+
"comment_id": "auto_increment",
|
|
333
|
+
"post_id": "required|integer",
|
|
334
|
+
"user_id": "required|integer",
|
|
335
|
+
"body": "required|string"
|
|
336
|
+
},
|
|
337
|
+
"pk": "comment_id",
|
|
338
|
+
"unique": ["comment_id"],
|
|
339
|
+
"parent": "posts"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### `parent` field rules
|
|
346
|
+
|
|
347
|
+
- `"parent": null` → top-level route: `/comments/`
|
|
348
|
+
- `"parent": "posts"` → nested route: `/posts/:post_id/comments/` (also mounted at top-level for direct access)
|
|
349
|
+
- **Do NOT use system tables as parents** (`users`, `tenants`, `roles`, `permissions`, `sessions`, `accounts`, `auth_tokens`). They are cross-cutting and referenced via FK columns — not route hierarchies. Only use `parent` for true domain hierarchies: `posts → comments`, `orders → order_items`, `projects → tasks`.
|
|
350
|
+
|
|
351
|
+
### Column Rules
|
|
352
|
+
|
|
353
|
+
Format: `(required|)?(string|integer|numeric|boolean|object|datetime|auto_increment)`
|
|
354
|
+
|
|
355
|
+
Include ALL columns in schema (PK, timestamps, softDelete). The generator auto-excludes them from model `structure`.
|
|
356
|
+
|
|
357
|
+
### Table Fields
|
|
358
|
+
|
|
359
|
+
| Field | Required | Description |
|
|
360
|
+
| ------------ | -------- | ------------------------------------------------ |
|
|
361
|
+
| `columns` | Yes | All columns including PK, timestamps, softDelete |
|
|
362
|
+
| `pk` | Yes | Primary key (convention: `<table>_id`) |
|
|
363
|
+
| `unique` | No | Unique constraint columns (default: `[pk]`) |
|
|
364
|
+
| `softDelete` | No | Column name for soft-delete |
|
|
365
|
+
| `timestamps` | No | `{ created_at, modified_at }` column mapping |
|
|
366
|
+
| `parent` | No | Parent table for route nesting, or `null` |
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Connection Configs (quick reference)
|
|
371
|
+
|
|
372
|
+
For full details (env vars, upsert behavior, notes), read the adapter reference file.
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
// MySQL / MariaDB (default adapter)
|
|
376
|
+
init("mysql");
|
|
377
|
+
db.connect({
|
|
378
|
+
host,
|
|
379
|
+
port: 3306,
|
|
380
|
+
user,
|
|
381
|
+
password,
|
|
382
|
+
database,
|
|
383
|
+
connectionLimit: 100,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// PostgreSQL — see references/postgres.md
|
|
387
|
+
init("postgres");
|
|
388
|
+
db.connect({ host, port: 5432, user, password, database });
|
|
389
|
+
|
|
390
|
+
// SQLite3 — see references/sqlite3.md
|
|
391
|
+
init("sqlite3");
|
|
392
|
+
db.connect({ database: "./file.db" }); // or ":memory:"
|
|
393
|
+
|
|
394
|
+
// MongoDB — see references/mongodb.md
|
|
395
|
+
init("mongodb");
|
|
396
|
+
db.connect({ host, port: 27017, username, password, database });
|
|
397
|
+
// or: db.connect({ uri: "mongodb://user:pass@host:27017/db" })
|
|
398
|
+
|
|
399
|
+
// MSSQL — see references/mssql.md (db.connect is async — use await)
|
|
400
|
+
init("mssql");
|
|
401
|
+
await db.connect({
|
|
402
|
+
server: host,
|
|
403
|
+
port: 1433,
|
|
404
|
+
user,
|
|
405
|
+
password,
|
|
406
|
+
database,
|
|
407
|
+
options: { encrypt: false, trustServerCertificate: true },
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Oracle — see references/oracle.md
|
|
411
|
+
init("oracle");
|
|
412
|
+
db.connect({ host, port: 1521, user, password, database });
|
|
413
|
+
|
|
414
|
+
// Redis — see references/redis.md
|
|
415
|
+
init("redis");
|
|
416
|
+
db.connect({ host, port: 6379, password });
|
|
417
|
+
|
|
418
|
+
// DynamoDB — see references/dynamodb.md
|
|
419
|
+
init("dynamodb");
|
|
420
|
+
db.connect({ region, endpoint, accessKeyId, secretAccessKey });
|
|
421
|
+
|
|
422
|
+
// CockroachDB — see references/cockroachdb.md
|
|
423
|
+
init("cockroachdb");
|
|
424
|
+
db.connect({ host, port: 26257, user, password, database });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Environment Variables by Database
|
|
430
|
+
|
|
431
|
+
| Database | Variables |
|
|
432
|
+
| ----------- | ---------------------------------------------------------------------- |
|
|
433
|
+
| mysql | `PORT DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
|
|
434
|
+
| mariadb | `PORT DB_HOST DB_PORT=3306 DB_NAME DB_USER DB_PASS` |
|
|
435
|
+
| postgres | `PORT DB_HOST DB_PORT=5432 DB_NAME DB_USER DB_PASS` |
|
|
436
|
+
| cockroachdb | `PORT DB_HOST DB_PORT=26257 DB_NAME DB_USER DB_PASS` |
|
|
437
|
+
| sqlite3 | `PORT DB_NAME=./data/data.db` |
|
|
438
|
+
| mongodb | `PORT DB_HOST DB_PORT=27017 DB_NAME DB_USER DB_PASS` |
|
|
439
|
+
| mssql | `PORT DB_HOST DB_PORT=1433 DB_NAME DB_USER DB_PASS` |
|
|
440
|
+
| oracle | `PORT DB_HOST DB_PORT=1521 DB_NAME DB_USER DB_PASS` |
|
|
441
|
+
| redis | `PORT DB_HOST DB_PORT=6379 DB_PASS` |
|
|
442
|
+
| dynamodb | `PORT AWS_REGION AWS_ENDPOINT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY` |
|
|
443
|
+
|
|
444
|
+
When `session=redis` and database ≠ redis: adds `REDIS_HOST REDIS_PORT REDIS_PASS`.
|
|
445
|
+
When `logger=true`: adds `APP_NAME LOG_LEVEL LOKI_HOST`.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Rules
|
|
450
|
+
|
|
451
|
+
1. `init()` before `db.connect()`. Don't destructure `db` before `init()` — it's a getter.
|
|
452
|
+
2. Generated projects are ESM. The library is CJS. Use default import: `import dbModelRouter from "db-model-router"; const { init, db } = dbModelRouter;`
|
|
453
|
+
3. `model structure` excludes: PK, timestamps, soft-delete, and `auto_increment` cols — the generator handles this.
|
|
454
|
+
4. `update()`/`patch()` require PK in payload. `upsert()` PK is optional.
|
|
455
|
+
5. `findOne()` returns `false` on no match. `byId()` returns `null`.
|
|
456
|
+
6. Bulk ops wrap in `{ data: [...] }`. Single ops use flat object.
|
|
457
|
+
7. Timestamps auto-stripped from payloads — DB handles defaults/triggers.
|
|
458
|
+
8. `safeDelete` makes `remove()` soft-delete; all reads auto-filter deleted rows.
|
|
459
|
+
9. `list()` defaults: page=0, size=30. `sort` array: `["-col"]` for DESC.
|
|
460
|
+
10. `global.db` is set by `commons/db.js` — accessible anywhere without imports.
|
|
461
|
+
11. Logger dynamically loads `winston-loki` only when `LOKI_HOST` env var is set.
|
|
462
|
+
12. Docker passwords are randomly generated and shared between `.env` and `docker-compose.yml`.
|
|
463
|
+
13. PK convention: `<table>_id` (e.g. `user_id`, `post_id`). Include ALL columns in schema.
|
|
464
|
+
14. Use `parent` only for domain hierarchies (e.g. `posts → comments`), not system tables.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CockroachDB Adapter
|
|
2
|
+
|
|
3
|
+
Uses the [pg](https://www.npmjs.com/package/pg) driver (CockroachDB is PostgreSQL wire-compatible).
|
|
4
|
+
|
|
5
|
+
## Connection
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { init, db, model, route } = require("db-model-router");
|
|
9
|
+
init("cockroachdb");
|
|
10
|
+
|
|
11
|
+
db.connect({
|
|
12
|
+
host: "localhost",
|
|
13
|
+
port: 26257,
|
|
14
|
+
database: "defaultdb",
|
|
15
|
+
user: "root",
|
|
16
|
+
password: "",
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Environment Variables
|
|
21
|
+
|
|
22
|
+
```env
|
|
23
|
+
CRDB_HOST=localhost
|
|
24
|
+
CRDB_PORT=26257
|
|
25
|
+
CRDB_NAME=defaultdb
|
|
26
|
+
CRDB_USER=root
|
|
27
|
+
CRDB_PASS=
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
- `SERIAL` columns use CockroachDB's `unique_rowid()` which generates INT8 values
|
|
33
|
+
- Large INT8 values that exceed `Number.MAX_SAFE_INTEGER` are kept as strings to preserve precision
|
|
34
|
+
- Includes automatic retry logic for transient connection errors
|
|
35
|
+
- Uses `ON CONFLICT` for upsert operations
|
|
36
|
+
- Run with `--insecure` flag for local development (see docker-compose.yml)
|
|
37
|
+
|
|
38
|
+
## Table Creation
|
|
39
|
+
|
|
40
|
+
```sql
|
|
41
|
+
CREATE TABLE users (
|
|
42
|
+
id SERIAL PRIMARY KEY,
|
|
43
|
+
name VARCHAR NOT NULL,
|
|
44
|
+
email VARCHAR NOT NULL,
|
|
45
|
+
age INTEGER NOT NULL
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
[← Back to main docs](../../README.md)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# DynamoDB Adapter
|
|
2
|
+
|
|
3
|
+
Uses [@aws-sdk/client-dynamodb](https://www.npmjs.com/package/@aws-sdk/client-dynamodb) and [@aws-sdk/lib-dynamodb](https://www.npmjs.com/package/@aws-sdk/lib-dynamodb).
|
|
4
|
+
|
|
5
|
+
## Connection
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { init, db, model, route } = require("db-model-router");
|
|
9
|
+
init("dynamodb");
|
|
10
|
+
|
|
11
|
+
db.connect({
|
|
12
|
+
region: "us-east-1",
|
|
13
|
+
// For local development (DynamoDB Local):
|
|
14
|
+
endpoint: "http://localhost:8000",
|
|
15
|
+
accessKeyId: "fakeAccessKey",
|
|
16
|
+
secretAccessKey: "fakeSecretKey",
|
|
17
|
+
primaryKey: "id",
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Environment Variables
|
|
22
|
+
|
|
23
|
+
```env
|
|
24
|
+
DYNAMODB_HOST=localhost
|
|
25
|
+
DYNAMODB_PORT=8000
|
|
26
|
+
DYNAMODB_REGION=us-east-1
|
|
27
|
+
DYNAMODB_ACCESS_KEY=fakeAccessKey
|
|
28
|
+
DYNAMODB_SECRET_KEY=fakeSecretKey
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Notes
|
|
32
|
+
|
|
33
|
+
- Tables must be created beforehand (the adapter does not create tables)
|
|
34
|
+
- Auto-generates UUID primary keys for records missing the PK field
|
|
35
|
+
- Filter operators are mapped to DynamoDB FilterExpression syntax
|
|
36
|
+
- `like` uses `contains()`, `in` uses `IN ()`
|
|
37
|
+
- All filtering and pagination happen via `Scan` with in-memory post-processing
|
|
38
|
+
- Batch writes are chunked into groups of 25 (DynamoDB limit)
|
|
39
|
+
- `UpdateCommand` is used for upsert operations
|
|
40
|
+
- Best suited for serverless / AWS-native architectures
|
|
41
|
+
|
|
42
|
+
## Table Creation (AWS CLI)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
aws dynamodb create-table \
|
|
46
|
+
--table-name users \
|
|
47
|
+
--attribute-definitions AttributeName=id,AttributeType=S \
|
|
48
|
+
--key-schema AttributeName=id,KeyType=HASH \
|
|
49
|
+
--billing-mode PAY_PER_REQUEST \
|
|
50
|
+
--endpoint-url http://localhost:8000
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
[← Back to main docs](../../README.md)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# MongoDB Adapter
|
|
2
|
+
|
|
3
|
+
Uses the official [mongodb](https://www.npmjs.com/package/mongodb) Node.js driver.
|
|
4
|
+
|
|
5
|
+
## Connection
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { init, db, model, route } = require("db-model-router");
|
|
9
|
+
init("mongodb");
|
|
10
|
+
|
|
11
|
+
db.connect({
|
|
12
|
+
host: "localhost",
|
|
13
|
+
port: 27017,
|
|
14
|
+
database: "my_app",
|
|
15
|
+
// or with auth:
|
|
16
|
+
username: "admin",
|
|
17
|
+
password: "secret",
|
|
18
|
+
// or with a full URI:
|
|
19
|
+
uri: "mongodb://admin:secret@localhost:27017",
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Environment Variables
|
|
24
|
+
|
|
25
|
+
```env
|
|
26
|
+
MONGO_HOST=localhost
|
|
27
|
+
MONGO_PORT=27017
|
|
28
|
+
MONGO_DB=test_db
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Notes
|
|
32
|
+
|
|
33
|
+
- Primary key is `_id` (MongoDB's default ObjectId)
|
|
34
|
+
- String `_id` values matching the 24-char hex format are auto-converted to `ObjectId`
|
|
35
|
+
- Filter operators are mapped to MongoDB query operators (`$eq`, `$regex`, `$in`, etc.)
|
|
36
|
+
- `like` uses `$regex` with case-insensitive matching
|
|
37
|
+
- No schema/table creation needed — collections are created on first insert
|
|
38
|
+
|
|
39
|
+
## Model Definition
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
const users = model(
|
|
43
|
+
db,
|
|
44
|
+
"users",
|
|
45
|
+
{
|
|
46
|
+
_id: "string",
|
|
47
|
+
name: "required|string",
|
|
48
|
+
email: "required|string",
|
|
49
|
+
age: "required|integer",
|
|
50
|
+
},
|
|
51
|
+
"_id",
|
|
52
|
+
["_id"],
|
|
53
|
+
);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
[← Back to main docs](../../README.md)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# MSSQL (SQL Server) Adapter
|
|
2
|
+
|
|
3
|
+
Uses the [mssql](https://www.npmjs.com/package/mssql) driver with tedious.
|
|
4
|
+
|
|
5
|
+
## Connection
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { init, db, model, route } = require("db-model-router");
|
|
9
|
+
init("mssql");
|
|
10
|
+
|
|
11
|
+
await db.connect({
|
|
12
|
+
server: "localhost",
|
|
13
|
+
port: 1433,
|
|
14
|
+
database: "master",
|
|
15
|
+
user: "sa",
|
|
16
|
+
password: "Password123!",
|
|
17
|
+
options: {
|
|
18
|
+
encrypt: false,
|
|
19
|
+
trustServerCertificate: true,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Note: `db.connect()` is async for MSSQL — use `await`.
|
|
25
|
+
|
|
26
|
+
## Environment Variables
|
|
27
|
+
|
|
28
|
+
```env
|
|
29
|
+
MSSQL_HOST=localhost
|
|
30
|
+
MSSQL_PORT=1433
|
|
31
|
+
MSSQL_DB=master
|
|
32
|
+
MSSQL_USER=sa
|
|
33
|
+
MSSQL_PASSWORD=Password123!
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- Column names are escaped with `[brackets]`
|
|
39
|
+
- Uses `MERGE` statements for upsert operations
|
|
40
|
+
- IDENTITY columns are auto-excluded from INSERT in MERGE statements
|
|
41
|
+
- Pagination uses `OFFSET ... ROWS FETCH NEXT ... ROWS ONLY`
|
|
42
|
+
- `OUTPUT INSERTED.*` is used to return inserted rows
|
|
43
|
+
|
|
44
|
+
## Table Creation
|
|
45
|
+
|
|
46
|
+
```sql
|
|
47
|
+
CREATE TABLE users (
|
|
48
|
+
id INT IDENTITY(1,1) PRIMARY KEY,
|
|
49
|
+
name NVARCHAR(255),
|
|
50
|
+
email NVARCHAR(255),
|
|
51
|
+
age INT
|
|
52
|
+
);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
[← Back to main docs](../../README.md)
|