db-model-router 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +7 -0
- package/LICENSE +201 -0
- package/README.md +505 -0
- package/docker-compose.yml +141 -0
- package/docs/README.md +208 -0
- package/docs/SKILL.md +202 -0
- package/docs/adapters/cockroachdb.md +49 -0
- package/docs/adapters/dynamodb.md +53 -0
- package/docs/adapters/mongodb.md +56 -0
- package/docs/adapters/mssql.md +55 -0
- package/docs/adapters/oracle.md +52 -0
- package/docs/adapters/postgres.md +50 -0
- package/docs/adapters/redis.md +53 -0
- package/docs/adapters/sqlite3.md +43 -0
- package/package.json +109 -0
- package/src/cli/generate-app.js +359 -0
- package/src/cli/generate-model.js +760 -0
- package/src/cli/generate-openapi.js +237 -0
- package/src/cli/generate-route.js +346 -0
- package/src/cockroachdb/db.js +563 -0
- package/src/commons/function.js +165 -0
- package/src/commons/model.js +444 -0
- package/src/commons/route.js +214 -0
- package/src/commons/validator.js +172 -0
- package/src/dynamodb/db.js +552 -0
- package/src/index.js +57 -0
- package/src/mongodb/db.js +381 -0
- package/src/mssql/db.js +461 -0
- package/src/mysql/db.js +527 -0
- package/src/oracle/db.js +855 -0
- package/src/oracle/sql_translator.js +406 -0
- package/src/postgres/db.js +666 -0
- package/src/postgres/ddl_translator.js +69 -0
- package/src/postgres/sql_translator.js +396 -0
- package/src/redis/db.js +448 -0
- package/src/serve.js +90 -0
- package/src/sqlite3/db.js +346 -0
package/README.md
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# rest-router
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Supported Adapters
|
|
6
|
+
|
|
7
|
+
| Adapter | Module Key | Driver | Install |
|
|
8
|
+
| --------------------------------------------- | ------------- | ------------------------ | ---------------------------------------------------------------------- |
|
|
9
|
+
| [MySQL](#mysql-example) | `mysql` | mysql2 | `npm i db-model-router mysql2` |
|
|
10
|
+
| [PostgreSQL](./docs/adapters/postgres.md) | `postgres` | pg | `npm i db-model-router pg` |
|
|
11
|
+
| [SQLite3](./docs/adapters/sqlite3.md) | `sqlite3` | better-sqlite3 | `npm i db-model-router better-sqlite3` |
|
|
12
|
+
| [MongoDB](./docs/adapters/mongodb.md) | `mongodb` | mongodb | `npm i db-model-router mongodb` |
|
|
13
|
+
| [MSSQL](./docs/adapters/mssql.md) | `mssql` | mssql | `npm i db-model-router mssql` |
|
|
14
|
+
| [CockroachDB](./docs/adapters/cockroachdb.md) | `cockroachdb` | pg | `npm i db-model-router pg` |
|
|
15
|
+
| [Oracle](./docs/adapters/oracle.md) | `oracle` | oracledb | `npm i db-model-router oracledb` |
|
|
16
|
+
| [Redis](./docs/adapters/redis.md) | `redis` | ioredis | `npm i db-model-router ioredis` |
|
|
17
|
+
| [DynamoDB](./docs/adapters/dynamodb.md) | `dynamodb` | @aws-sdk/client-dynamodb | `npm i db-model-router @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb` |
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Install the core package, your preferred Express framework, and the driver for your database:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Pick your Express framework (one of the two)
|
|
25
|
+
npm install express
|
|
26
|
+
# OR for ~6x faster performance:
|
|
27
|
+
npm install ultimate-express
|
|
28
|
+
|
|
29
|
+
# Then install db-model-router + your database driver:
|
|
30
|
+
|
|
31
|
+
# MySQL (default)
|
|
32
|
+
npm install db-model-router mysql2
|
|
33
|
+
|
|
34
|
+
# PostgreSQL / CockroachDB
|
|
35
|
+
npm install db-model-router pg
|
|
36
|
+
|
|
37
|
+
# SQLite3
|
|
38
|
+
npm install db-model-router better-sqlite3
|
|
39
|
+
|
|
40
|
+
# MongoDB
|
|
41
|
+
npm install db-model-router mongodb
|
|
42
|
+
|
|
43
|
+
# MSSQL
|
|
44
|
+
npm install db-model-router mssql
|
|
45
|
+
|
|
46
|
+
# Oracle
|
|
47
|
+
npm install db-model-router oracledb
|
|
48
|
+
|
|
49
|
+
# Redis
|
|
50
|
+
npm install db-model-router ioredis
|
|
51
|
+
|
|
52
|
+
# DynamoDB
|
|
53
|
+
npm install db-model-router @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
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
|
+
|
|
58
|
+
## MySQL Example
|
|
59
|
+
|
|
60
|
+
### 1. Connect
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
const { init, db, model, route } = require("db-model-router");
|
|
64
|
+
|
|
65
|
+
// Default adapter is mysql, so init() is optional
|
|
66
|
+
db.connect({
|
|
67
|
+
host: "localhost",
|
|
68
|
+
port: 3306,
|
|
69
|
+
user: "root",
|
|
70
|
+
password: "password",
|
|
71
|
+
database: "my_app",
|
|
72
|
+
connectionLimit: 100,
|
|
73
|
+
charset: "utf8mb4",
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2. Define a Model
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
const users = model(
|
|
81
|
+
db,
|
|
82
|
+
"users", // table name
|
|
83
|
+
{
|
|
84
|
+
// schema definition
|
|
85
|
+
id: "integer", // auto-increment PK (excluded from inserts)
|
|
86
|
+
name: "required|string",
|
|
87
|
+
email: "required|string",
|
|
88
|
+
age: "required|integer",
|
|
89
|
+
meta: "object", // stored as JSON
|
|
90
|
+
is_deleted: "boolean",
|
|
91
|
+
},
|
|
92
|
+
"id", // primary key column
|
|
93
|
+
["id"], // unique key columns
|
|
94
|
+
{ safeDelete: "is_deleted" }, // optional: soft-delete column
|
|
95
|
+
);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Schema types: `string`, `integer`, `boolean`, `object`. Prefix with `required|` to enforce on insert/update.
|
|
99
|
+
|
|
100
|
+
### 3. Mount REST Routes
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
// Works with either express or ultimate-express
|
|
104
|
+
const express = require("express"); // or require("ultimate-express")
|
|
105
|
+
const app = express();
|
|
106
|
+
app.use(express.json());
|
|
107
|
+
|
|
108
|
+
app.use("/users", route(users));
|
|
109
|
+
app.listen(3000);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This creates 8 endpoints:
|
|
113
|
+
|
|
114
|
+
| Method | Path | Description |
|
|
115
|
+
| ------ | ------------ | ------------------------------- |
|
|
116
|
+
| GET | `/users/:id` | Get one record by PK |
|
|
117
|
+
| POST | `/users/add` | Insert a single record |
|
|
118
|
+
| PUT | `/users/:id` | Update a single record |
|
|
119
|
+
| DELETE | `/users/:id` | Delete a single record |
|
|
120
|
+
| GET | `/users/` | List with pagination |
|
|
121
|
+
| POST | `/users/` | Bulk insert (`{ data: [...] }`) |
|
|
122
|
+
| PUT | `/users/` | Bulk update (`{ data: [...] }`) |
|
|
123
|
+
| DELETE | `/users/` | Bulk delete |
|
|
124
|
+
|
|
125
|
+
### 4. Payload Override
|
|
126
|
+
|
|
127
|
+
Inject values from the request into every payload (useful for multi-tenant apps):
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
app.use((req, res, next) => {
|
|
131
|
+
req.user = { user_id: 42 };
|
|
132
|
+
next();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
app.use("/users", route(users, { user_id: "user.user_id" }));
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Every insert/update/query will have `user_id` set from `req.user.user_id`.
|
|
139
|
+
|
|
140
|
+
## Model API
|
|
141
|
+
|
|
142
|
+
All model methods are async (except SQLite3 which is synchronous under the hood).
|
|
143
|
+
|
|
144
|
+
### insert(data)
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// Single insert — returns the full inserted record
|
|
148
|
+
const user = await users.insert({
|
|
149
|
+
name: "Alice",
|
|
150
|
+
email: "alice@example.com",
|
|
151
|
+
age: 30,
|
|
152
|
+
});
|
|
153
|
+
// => { id: 1, name: "Alice", email: "alice@example.com", age: 30 }
|
|
154
|
+
|
|
155
|
+
// Bulk insert — returns row count
|
|
156
|
+
const result = await users.insert({
|
|
157
|
+
data: [
|
|
158
|
+
{ name: "Bob", email: "bob@example.com", age: 25 },
|
|
159
|
+
{ name: "Charlie", email: "charlie@example.com", age: 35 },
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
// => { rows: 2, message: "2 Userss are saved", type: "success" }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### update(data)
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
// Single update — returns the updated record
|
|
169
|
+
const updated = await users.update({
|
|
170
|
+
id: 1,
|
|
171
|
+
name: "Alice Updated",
|
|
172
|
+
email: "alice_v2@example.com",
|
|
173
|
+
age: 31,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Bulk update
|
|
177
|
+
const result = await users.update({
|
|
178
|
+
data: [
|
|
179
|
+
{ id: 1, name: "Alice V3", email: "alice@example.com", age: 32 },
|
|
180
|
+
{ id: 2, name: "Bob V2", email: "bob@example.com", age: 26 },
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### byId(id)
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
const user = await users.byId(1);
|
|
189
|
+
// => { id: 1, name: "Alice", ... } or null
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### find(filter)
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
const result = await users.find({ name: "Alice" });
|
|
196
|
+
// => { data: [{ id: 1, name: "Alice", ... }], count: 1 }
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### findOne(filter)
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
const user = await users.findOne({ email: "alice@example.com" });
|
|
203
|
+
// => { id: 1, ... } or false
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### list(options)
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
const page = await users.list({ page: 0, size: 10 });
|
|
210
|
+
// => { data: [...], count: 100 }
|
|
211
|
+
|
|
212
|
+
// With filter
|
|
213
|
+
const filtered = await users.list({ name: "Ali", page: 0 });
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### remove(idOrFilter)
|
|
217
|
+
|
|
218
|
+
```js
|
|
219
|
+
// By ID
|
|
220
|
+
await users.remove(1);
|
|
221
|
+
|
|
222
|
+
// By filter
|
|
223
|
+
await users.remove({ name: "Bob" });
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Filter System
|
|
227
|
+
|
|
228
|
+
Filters use a nested array structure: `[OR_groups[AND_conditions[column, operator, value]]]`
|
|
229
|
+
|
|
230
|
+
Supported operators: `=`, `like`, `not like`, `in`, `not in`, `<`, `>`, `<=`, `>=`, `!=`
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
// Find users named Alice OR aged > 30
|
|
234
|
+
const result = await db.get("users", [
|
|
235
|
+
[["name", "=", "Alice"]],
|
|
236
|
+
[["age", ">", 30]],
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
// Find users named Alice AND aged 30
|
|
240
|
+
const result = await db.get("users", [
|
|
241
|
+
[
|
|
242
|
+
["name", "=", "Alice"],
|
|
243
|
+
["age", "=", 30],
|
|
244
|
+
],
|
|
245
|
+
]);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Switching Adapters
|
|
249
|
+
|
|
250
|
+
To use a different database, call `init()` before `db.connect()`:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
const { init, db, model, route } = require("db-model-router");
|
|
254
|
+
|
|
255
|
+
init("postgres"); // or "mongodb", "sqlite3", "mssql", etc.
|
|
256
|
+
|
|
257
|
+
db.connect({
|
|
258
|
+
host: "localhost",
|
|
259
|
+
port: 5432,
|
|
260
|
+
user: "postgres",
|
|
261
|
+
password: "password",
|
|
262
|
+
database: "my_app",
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The model and route APIs remain identical across all adapters. See the individual adapter docs for connection options:
|
|
267
|
+
|
|
268
|
+
- [PostgreSQL](./docs/adapters/postgres.md)
|
|
269
|
+
- [SQLite3](./docs/adapters/sqlite3.md)
|
|
270
|
+
- [MongoDB](./docs/adapters/mongodb.md)
|
|
271
|
+
- [MSSQL](./docs/adapters/mssql.md)
|
|
272
|
+
- [CockroachDB](./docs/adapters/cockroachdb.md)
|
|
273
|
+
- [Oracle](./docs/adapters/oracle.md)
|
|
274
|
+
- [Redis](./docs/adapters/redis.md)
|
|
275
|
+
- [DynamoDB](./docs/adapters/dynamodb.md)
|
|
276
|
+
|
|
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
|
+
rest-router-generate-app --type mysql --env .env
|
|
288
|
+
|
|
289
|
+
# SQLite3 into a specific directory
|
|
290
|
+
rest-router-generate-app --type sqlite3 --database ./myapp.db --output ./my-api
|
|
291
|
+
|
|
292
|
+
# Postgres with specific tables and relationships
|
|
293
|
+
rest-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
|
+
rest-router-generate-model --type mysql --host localhost --database mydb --user root --password secret
|
|
352
|
+
|
|
353
|
+
# Using an .env file
|
|
354
|
+
rest-router-generate-model --type postgres --env .env --output ./src/models
|
|
355
|
+
|
|
356
|
+
# SQLite3
|
|
357
|
+
rest-router-generate-model --type sqlite3 --database ./myapp.db --output ./models
|
|
358
|
+
|
|
359
|
+
# Only specific tables
|
|
360
|
+
rest-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
|
+
rest-router-generate-route --models ./models --output ./routes
|
|
401
|
+
|
|
402
|
+
# Auto-generate models + routes in one step
|
|
403
|
+
rest-router-generate-route --type mysql --env .env --models ./models --output ./routes
|
|
404
|
+
|
|
405
|
+
# SQLite3 one-liner
|
|
406
|
+
rest-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
|
+
rest-router-generate-route --type mysql --env .env --tables users,posts,posts.comments
|
|
432
|
+
|
|
433
|
+
# Same via generate-app
|
|
434
|
+
rest-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
|
+
## Environment Setup (Docker)
|
|
479
|
+
|
|
480
|
+
A `docker-compose.yml` is included for running all supported databases locally:
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
docker compose up -d
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Per-adapter `.env` files live in `env/`:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
npm run test:mysql # uses env/.env.mysql
|
|
490
|
+
npm run test:postgres # uses env/.env.postgres
|
|
491
|
+
npm run test:mongodb # uses env/.env.mongodb
|
|
492
|
+
npm run test:redis # uses env/.env.redis
|
|
493
|
+
npm run test:mssql # uses env/.env.mssql
|
|
494
|
+
npm run test:cockroachdb # uses env/.env.cockroachdb
|
|
495
|
+
npm run test:dynamodb # uses env/.env.dynamodb
|
|
496
|
+
npm run test:sqlite3 # uses env/.env.sqlite3 (in-memory, no Docker needed)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## License
|
|
500
|
+
|
|
501
|
+
Apache-2.0
|
|
502
|
+
|
|
503
|
+
## LLM Skill Reference
|
|
504
|
+
|
|
505
|
+
For AI/LLM integration, see the [Skill Reference](./docs/SKILL.md) — a structured document covering the full API surface, patterns, constraints, and connection configs for all adapters.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
networks:
|
|
2
|
+
db-network:
|
|
3
|
+
driver: bridge
|
|
4
|
+
|
|
5
|
+
services:
|
|
6
|
+
mysql:
|
|
7
|
+
image: mysql:8.0
|
|
8
|
+
container_name: rest-router-mysql
|
|
9
|
+
restart: unless-stopped
|
|
10
|
+
ports:
|
|
11
|
+
- "3306:3306"
|
|
12
|
+
environment:
|
|
13
|
+
MYSQL_ROOT_PASSWORD: password
|
|
14
|
+
MYSQL_DATABASE: test_db
|
|
15
|
+
command: --default-authentication-plugin=mysql_native_password
|
|
16
|
+
healthcheck:
|
|
17
|
+
test:
|
|
18
|
+
["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-ppassword"]
|
|
19
|
+
interval: 10s
|
|
20
|
+
timeout: 5s
|
|
21
|
+
retries: 5
|
|
22
|
+
start_period: 30s
|
|
23
|
+
networks:
|
|
24
|
+
- db-network
|
|
25
|
+
|
|
26
|
+
postgres:
|
|
27
|
+
image: postgres:16
|
|
28
|
+
container_name: rest-router-postgres
|
|
29
|
+
restart: unless-stopped
|
|
30
|
+
ports:
|
|
31
|
+
- "5432:5432"
|
|
32
|
+
environment:
|
|
33
|
+
POSTGRES_USER: postgres
|
|
34
|
+
POSTGRES_PASSWORD: password
|
|
35
|
+
POSTGRES_DB: test_db
|
|
36
|
+
healthcheck:
|
|
37
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
38
|
+
interval: 10s
|
|
39
|
+
timeout: 5s
|
|
40
|
+
retries: 5
|
|
41
|
+
start_period: 15s
|
|
42
|
+
networks:
|
|
43
|
+
- db-network
|
|
44
|
+
|
|
45
|
+
mongodb:
|
|
46
|
+
image: mongo:7
|
|
47
|
+
container_name: rest-router-mongodb
|
|
48
|
+
restart: unless-stopped
|
|
49
|
+
ports:
|
|
50
|
+
- "27017:27017"
|
|
51
|
+
healthcheck:
|
|
52
|
+
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
|
53
|
+
interval: 10s
|
|
54
|
+
timeout: 5s
|
|
55
|
+
retries: 5
|
|
56
|
+
start_period: 15s
|
|
57
|
+
networks:
|
|
58
|
+
- db-network
|
|
59
|
+
|
|
60
|
+
redis:
|
|
61
|
+
image: redis:7
|
|
62
|
+
container_name: rest-router-redis
|
|
63
|
+
restart: unless-stopped
|
|
64
|
+
ports:
|
|
65
|
+
- "6379:6379"
|
|
66
|
+
healthcheck:
|
|
67
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
68
|
+
interval: 10s
|
|
69
|
+
timeout: 5s
|
|
70
|
+
retries: 5
|
|
71
|
+
start_period: 10s
|
|
72
|
+
networks:
|
|
73
|
+
- db-network
|
|
74
|
+
|
|
75
|
+
cockroachdb:
|
|
76
|
+
image: cockroachdb/cockroach:latest
|
|
77
|
+
container_name: rest-router-cockroachdb
|
|
78
|
+
restart: unless-stopped
|
|
79
|
+
ports:
|
|
80
|
+
- "26257:26257"
|
|
81
|
+
- "8080:8080"
|
|
82
|
+
command: start-single-node --insecure
|
|
83
|
+
healthcheck:
|
|
84
|
+
test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"]
|
|
85
|
+
interval: 10s
|
|
86
|
+
timeout: 5s
|
|
87
|
+
retries: 5
|
|
88
|
+
start_period: 20s
|
|
89
|
+
networks:
|
|
90
|
+
- db-network
|
|
91
|
+
|
|
92
|
+
mssql:
|
|
93
|
+
image: mcr.microsoft.com/mssql/server
|
|
94
|
+
container_name: rest-router-mssql
|
|
95
|
+
restart: unless-stopped
|
|
96
|
+
ports:
|
|
97
|
+
- "1433:1433"
|
|
98
|
+
environment:
|
|
99
|
+
ACCEPT_EULA: "Y"
|
|
100
|
+
MSSQL_SA_PASSWORD: "Password123!"
|
|
101
|
+
healthcheck:
|
|
102
|
+
test: ["CMD-SHELL", "/opt/mssql-tools*/bin/sqlcmd -S localhost -U sa -P 'Password123!' -Q 'SELECT 1' || exit 1"]
|
|
103
|
+
interval: 10s
|
|
104
|
+
timeout: 5s
|
|
105
|
+
retries: 10
|
|
106
|
+
start_period: 30s
|
|
107
|
+
networks:
|
|
108
|
+
- db-network
|
|
109
|
+
|
|
110
|
+
dynamodb:
|
|
111
|
+
image: amazon/dynamodb-local
|
|
112
|
+
container_name: rest-router-dynamodb
|
|
113
|
+
restart: unless-stopped
|
|
114
|
+
ports:
|
|
115
|
+
- "8000:8000"
|
|
116
|
+
command: -jar DynamoDBLocal.jar -sharedDb
|
|
117
|
+
healthcheck:
|
|
118
|
+
test: ["CMD-SHELL", "curl -f http://localhost:8000 || exit 1"]
|
|
119
|
+
interval: 10s
|
|
120
|
+
timeout: 5s
|
|
121
|
+
retries: 5
|
|
122
|
+
start_period: 10s
|
|
123
|
+
networks:
|
|
124
|
+
- db-network
|
|
125
|
+
|
|
126
|
+
oracle:
|
|
127
|
+
image: gvenzl/oracle-xe:21-slim
|
|
128
|
+
container_name: rest-router-oracle
|
|
129
|
+
restart: unless-stopped
|
|
130
|
+
ports:
|
|
131
|
+
- "1521:1521"
|
|
132
|
+
environment:
|
|
133
|
+
ORACLE_PASSWORD: oracle
|
|
134
|
+
healthcheck:
|
|
135
|
+
test: ["CMD-SHELL", "healthcheck.sh"]
|
|
136
|
+
interval: 30s
|
|
137
|
+
timeout: 10s
|
|
138
|
+
retries: 10
|
|
139
|
+
start_period: 90s
|
|
140
|
+
networks:
|
|
141
|
+
- db-network
|