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.
@@ -0,0 +1,50 @@
1
+ # PostgreSQL Adapter
2
+
3
+ Uses the [pg](https://www.npmjs.com/package/pg) driver with connection pooling.
4
+
5
+ ## Connection
6
+
7
+ ```js
8
+ const { init, db, model, route } = require("db-model-router");
9
+ init("postgres");
10
+
11
+ db.connect({
12
+ host: "localhost",
13
+ port: 5432,
14
+ user: "postgres",
15
+ password: "password",
16
+ database: "my_app",
17
+ connectionLimit: 50, // pool max
18
+ dateStrings: false, // set true to return dates as strings
19
+ });
20
+ ```
21
+
22
+ ## Environment Variables
23
+
24
+ ```env
25
+ PG_HOST=localhost
26
+ PG_PORT=5432
27
+ PG_USER=postgres
28
+ PG_PASSWORD=password
29
+ PG_DB=test_db
30
+ ```
31
+
32
+ ## Notes
33
+
34
+ - Supports MySQL-style `?` placeholders in raw `db.query()` calls — they are auto-translated to `$1, $2, ...`
35
+ - `SERIAL` / `BIGSERIAL` primary keys are auto-detected via `pg_index`
36
+ - `ON CONFLICT` is used for upsert operations
37
+ - Includes a SQL translator layer that converts common MySQL DDL/DML to PostgreSQL syntax
38
+
39
+ ## Table Creation
40
+
41
+ ```sql
42
+ CREATE TABLE users (
43
+ id SERIAL PRIMARY KEY,
44
+ name VARCHAR NOT NULL,
45
+ email VARCHAR NOT NULL,
46
+ age INTEGER NOT NULL
47
+ );
48
+ ```
49
+
50
+ [← Back to main docs](../README.md)
@@ -0,0 +1,53 @@
1
+ # Redis Adapter
2
+
3
+ Uses [ioredis](https://www.npmjs.com/package/ioredis). Records are stored as Redis hashes with key pattern `{table}:{id}`.
4
+
5
+ ## Connection
6
+
7
+ ```js
8
+ const { init, db, model, route } = require("db-model-router");
9
+ init("redis");
10
+
11
+ db.connect({
12
+ host: "localhost",
13
+ port: 6379,
14
+ password: "", // optional
15
+ db: 0, // Redis DB index, optional
16
+ primaryKey: "id", // field used as the hash key suffix
17
+ });
18
+ ```
19
+
20
+ ## Environment Variables
21
+
22
+ ```env
23
+ REDIS_HOST=localhost
24
+ REDIS_PORT=6379
25
+ ```
26
+
27
+ ## Notes
28
+
29
+ - Each record is a Redis hash at key `{table}:{primaryKey}`
30
+ - Auto-incrementing IDs use `INCR {table}:__seq`
31
+ - All filtering, sorting, and pagination happen in-memory (full SCAN)
32
+ - Redis stores all values as strings — numeric coercion is applied on read
33
+ - Nested objects are JSON-serialized into hash fields
34
+ - Best suited for small-to-medium datasets where Redis is already in the stack
35
+
36
+ ## Model Definition
37
+
38
+ ```js
39
+ const users = model(
40
+ db,
41
+ "users",
42
+ {
43
+ id: "string",
44
+ name: "required|string",
45
+ email: "required|string",
46
+ age: "required|integer",
47
+ },
48
+ "id",
49
+ ["id"],
50
+ );
51
+ ```
52
+
53
+ [← Back to main docs](../README.md)
@@ -0,0 +1,43 @@
1
+ # SQLite3 Adapter
2
+
3
+ Uses [better-sqlite3](https://www.npmjs.com/package/better-sqlite3) for synchronous, high-performance SQLite access.
4
+
5
+ ## Connection
6
+
7
+ ```js
8
+ const { init, db, model, route } = require("db-model-router");
9
+ init("sqlite3");
10
+
11
+ db.connect({ database: "./data.db" });
12
+ // or in-memory:
13
+ db.connect({ database: ":memory:" });
14
+ ```
15
+
16
+ ## Options
17
+
18
+ | Option | Description |
19
+ | --------------- | --------------------------- |
20
+ | `database` | File path or `:memory:` |
21
+ | `readonly` | Open in read-only mode |
22
+ | `fileMustExist` | Throw if file doesn't exist |
23
+
24
+ ## Notes
25
+
26
+ - All operations are synchronous (wrapped to match the async model API)
27
+ - WAL journal mode is enabled by default for better concurrency
28
+ - Uses `INSERT OR IGNORE` for conflict handling
29
+ - `ON CONFLICT ... DO UPDATE SET` for upsert
30
+ - No Docker container needed — runs in-process
31
+
32
+ ## Table Creation
33
+
34
+ ```sql
35
+ CREATE TABLE users (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ name TEXT NOT NULL,
38
+ email TEXT NOT NULL,
39
+ age INTEGER NOT NULL
40
+ );
41
+ ```
42
+
43
+ [← Back to main docs](../README.md)
package/package.json ADDED
@@ -0,0 +1,109 @@
1
+ {
2
+ "name": "db-model-router",
3
+ "version": "1.0.0",
4
+ "description": "Generative API Creation using mysql2 and express libraries in node js",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "generate-model": "src/cli/generate-model.js",
8
+ "generate-route": "src/cli/generate-route.js",
9
+ "generate-app": "src/cli/generate-app.js"
10
+ },
11
+ "scripts": {
12
+ "dev": "nodemon src/serve.js",
13
+ "test": "dotenv -e env/.env.default -- mocha --exit",
14
+ "test:sqlite3": "dotenv -e env/.env.sqlite3 -- mocha test/adapters/sqlite3.*.test.js test/properties/sqlite3.filter.property.test.js --timeout 15000 --exit",
15
+ "test:mysql": "dotenv -e env/.env.mysql -- mocha test/adapters/mysql.*.test.js test/properties/mysql.filter.property.test.js --timeout 15000 --exit",
16
+ "test:postgres": "dotenv -e env/.env.postgres -- mocha test/adapters/postgres.*.test.js test/properties/postgres.filter.property.test.js --timeout 15000 --exit",
17
+ "test:oracle": "dotenv -e env/.env.oracle -- mocha test/adapters/oracle.*.test.js test/properties/oracle.filter.property.test.js --timeout 30000 --exit",
18
+ "test:mongodb": "dotenv -e env/.env.mongodb -- mocha test/adapters/mongodb.*.test.js test/properties/mongodb.filter.property.test.js --timeout 15000 --exit",
19
+ "test:dynamodb": "dotenv -e env/.env.dynamodb -- mocha test/adapters/dynamodb.*.test.js test/properties/dynamodb.filter.property.test.js --timeout 15000 --exit",
20
+ "test:redis": "dotenv -e env/.env.redis -- mocha test/adapters/redis.*.test.js --timeout 15000 --exit",
21
+ "test:cockroachdb": "dotenv -e env/.env.cockroachdb -- mocha test/adapters/cockroachdb.*.test.js --timeout 15000 --exit",
22
+ "test:mssql": "dotenv -e env/.env.mssql -- mocha test/adapters/mssql.*.test.js --timeout 30000 --exit",
23
+ "test:properties": "mocha test/properties/*.property.test.js --timeout 30000 --exit",
24
+ "test:all": "mocha test/adapters/*.test.js test/properties/*.property.test.js test/function.test.js --timeout 30000 --exit"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/AvinashSKaranth/db-model-router"
29
+ },
30
+ "keywords": [
31
+ "mysql2",
32
+ "express",
33
+ "generative",
34
+ "rest",
35
+ "api"
36
+ ],
37
+ "author": "Avinash S Karanth",
38
+ "license": "Apache-2.0",
39
+ "bugs": {
40
+ "url": "https://github.com/AvinashSKaranth/db-model-router/issues"
41
+ },
42
+ "homepage": "https://github.com/AvinashSKaranth/db-model-router#readme",
43
+ "dependencies": {
44
+ "dotenv": "^10.0.0",
45
+ "lodash": "^4.17.21",
46
+ "node-input-validator": "^4.5.0"
47
+ },
48
+ "peerDependencies": {
49
+ "express": "^4.17.2 || ^5.0.0",
50
+ "ultimate-express": "^2.0.0",
51
+ "body-parser": "^1.20.0",
52
+ "@aws-sdk/client-dynamodb": "^3.1029.0",
53
+ "@aws-sdk/lib-dynamodb": "^3.1029.0",
54
+ "better-sqlite3": "^12.9.0",
55
+ "ioredis": "^5.10.1",
56
+ "mongodb": "^7.1.1",
57
+ "mssql": "^12.2.1",
58
+ "mysql2": "^3.14.4",
59
+ "oracledb": "^6.10.0",
60
+ "pg": "^8.20.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "express": {
64
+ "optional": true
65
+ },
66
+ "ultimate-express": {
67
+ "optional": true
68
+ },
69
+ "body-parser": {
70
+ "optional": true
71
+ },
72
+ "@aws-sdk/client-dynamodb": {
73
+ "optional": true
74
+ },
75
+ "@aws-sdk/lib-dynamodb": {
76
+ "optional": true
77
+ },
78
+ "better-sqlite3": {
79
+ "optional": true
80
+ },
81
+ "ioredis": {
82
+ "optional": true
83
+ },
84
+ "mongodb": {
85
+ "optional": true
86
+ },
87
+ "mssql": {
88
+ "optional": true
89
+ },
90
+ "mysql2": {
91
+ "optional": true
92
+ },
93
+ "oracledb": {
94
+ "optional": true
95
+ },
96
+ "pg": {
97
+ "optional": true
98
+ }
99
+ },
100
+ "devDependencies": {
101
+ "body-parser": "^1.20.0",
102
+ "dotenv-cli": "^11.0.0",
103
+ "express": "^4.21.0",
104
+ "faker": "^5.5.3",
105
+ "fast-check": "^4.6.0",
106
+ "mocha": "^11.7.2",
107
+ "supertest": "^6.2.4"
108
+ }
109
+ }
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const { execFileSync } = require("child_process");
6
+
7
+ const DB_TYPE_MAP = {
8
+ mysql: "mysql",
9
+ postgres: "postgres",
10
+ postgresql: "postgres",
11
+ sqlite3: "sqlite3",
12
+ mssql: "mssql",
13
+ oracle: "oracle",
14
+ cockroachdb: "cockroachdb",
15
+ };
16
+
17
+ const SUPPORTED_TYPES = Object.keys(DB_TYPE_MAP);
18
+
19
+ function ensureDir(dir) {
20
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
21
+ }
22
+
23
+ function generateAppJs(dbType) {
24
+ return `let express;
25
+ try { express = require("ultimate-express"); } catch (_) { express = require("express"); }
26
+ const { init, db } = require("db-model-router");
27
+ const logger = require("./middleware/logger");
28
+ const routes = require("./routes");
29
+
30
+ // Load environment variables
31
+ require("dotenv").config();
32
+
33
+ // Initialize database adapter
34
+ init("${dbType}");
35
+ db.connect({
36
+ host: process.env.DB_HOST || "localhost",
37
+ port: process.env.DB_PORT,
38
+ database: process.env.DB_NAME,
39
+ user: process.env.DB_USER,
40
+ password: process.env.DB_PASS,
41
+ });
42
+
43
+ const app = express();
44
+ const PORT = process.env.PORT || 3000;
45
+
46
+ // Middleware
47
+ app.use(express.json());
48
+ app.use(express.urlencoded({ extended: true }));
49
+ app.use(logger);
50
+
51
+ // Routes
52
+ app.use("/api", routes);
53
+
54
+ // Health check
55
+ app.get("/health", (req, res) => {
56
+ res.json({ status: "ok", timestamp: new Date().toISOString() });
57
+ });
58
+
59
+ // Error handler
60
+ app.use((err, req, res, next) => {
61
+ console.error(err.stack);
62
+ res.status(500).json({ type: "danger", message: "Internal Server Error" });
63
+ });
64
+
65
+ app.listen(PORT, () => {
66
+ console.log(\`Server running on port \${PORT}\`);
67
+ });
68
+
69
+ module.exports = app;
70
+ `;
71
+ }
72
+
73
+ function generateLoggerMiddleware() {
74
+ return `/**
75
+ * Simple request logger middleware.
76
+ * Logs method, URL, status code, and response time.
77
+ */
78
+ module.exports = function logger(req, res, next) {
79
+ const start = Date.now();
80
+ const { method, originalUrl } = req;
81
+
82
+ res.on("finish", () => {
83
+ const duration = Date.now() - start;
84
+ const status = res.statusCode;
85
+ const level = status >= 400 ? "WARN" : "INFO";
86
+ console.log(
87
+ \`[\${new Date().toISOString()}] [\${level}] \${method} \${originalUrl} \${status} \${duration}ms\`,
88
+ );
89
+ });
90
+
91
+ next();
92
+ };
93
+ `;
94
+ }
95
+
96
+ function generateEnvExample(dbType) {
97
+ const lines = [
98
+ "# Server",
99
+ "PORT=3000",
100
+ "",
101
+ "# Database",
102
+ `DB_TYPE=${dbType}`,
103
+ ];
104
+ switch (dbType) {
105
+ case "mysql":
106
+ lines.push(
107
+ "DB_HOST=localhost",
108
+ "DB_PORT=3306",
109
+ "DB_NAME=my_app",
110
+ "DB_USER=root",
111
+ "DB_PASS=password",
112
+ );
113
+ break;
114
+ case "postgres":
115
+ case "cockroachdb":
116
+ lines.push(
117
+ "DB_HOST=localhost",
118
+ `DB_PORT=${dbType === "cockroachdb" ? 26257 : 5432}`,
119
+ "DB_NAME=my_app",
120
+ "DB_USER=postgres",
121
+ "DB_PASS=password",
122
+ );
123
+ break;
124
+ case "sqlite3":
125
+ lines.push("DB_NAME=./data.db");
126
+ break;
127
+ case "mssql":
128
+ lines.push(
129
+ "DB_HOST=localhost",
130
+ "DB_PORT=1433",
131
+ "DB_NAME=my_app",
132
+ "DB_USER=sa",
133
+ "DB_PASS=password",
134
+ );
135
+ break;
136
+ case "oracle":
137
+ lines.push(
138
+ "DB_HOST=localhost",
139
+ "DB_PORT=1521",
140
+ "DB_NAME=my_app",
141
+ "DB_USER=system",
142
+ "DB_PASS=password",
143
+ );
144
+ break;
145
+ case "mongodb":
146
+ lines.push(
147
+ "DB_HOST=localhost",
148
+ "DB_PORT=27017",
149
+ "DB_NAME=my_app",
150
+ "DB_USER=",
151
+ "DB_PASS=",
152
+ );
153
+ break;
154
+ case "redis":
155
+ lines.push("DB_HOST=localhost", "DB_PORT=6379", "DB_PASS=");
156
+ break;
157
+ case "dynamodb":
158
+ lines.push(
159
+ "AWS_REGION=us-east-1",
160
+ "AWS_ENDPOINT=http://localhost:8000",
161
+ "AWS_ACCESS_KEY_ID=local",
162
+ "AWS_SECRET_ACCESS_KEY=local",
163
+ );
164
+ break;
165
+ default:
166
+ lines.push(
167
+ "DB_HOST=localhost",
168
+ "DB_PORT=3306",
169
+ "DB_NAME=my_app",
170
+ "DB_USER=root",
171
+ "DB_PASS=password",
172
+ );
173
+ }
174
+ return lines.join("\n") + "\n";
175
+ }
176
+
177
+ function generateGitignore() {
178
+ return `node_modules/
179
+ .env
180
+ *.db
181
+ `;
182
+ }
183
+
184
+ async function main() {
185
+ const args = parseArgs(process.argv.slice(2));
186
+
187
+ if (args.help) {
188
+ printUsage();
189
+ process.exit(0);
190
+ }
191
+
192
+ const dbType = DB_TYPE_MAP[(args.type || "").toLowerCase()];
193
+ if (!dbType) {
194
+ console.error(
195
+ `Error: --type is required. Supported: ${SUPPORTED_TYPES.join(", ")}`,
196
+ );
197
+ process.exit(1);
198
+ }
199
+
200
+ const outputDir = path.resolve(args.output || ".");
201
+ const modelsDir = path.join(outputDir, "models");
202
+ const routesDir = path.join(outputDir, "routes");
203
+ const middlewareDir = path.join(outputDir, "middleware");
204
+ const migrationsDir = path.join(outputDir, "migrations");
205
+ const sessionsDir = path.join(outputDir, "sessions");
206
+
207
+ console.log(`Scaffolding app in ${outputDir}...\n`);
208
+
209
+ // Create directories
210
+ for (const dir of [outputDir, middlewareDir, migrationsDir, sessionsDir]) {
211
+ ensureDir(dir);
212
+ }
213
+
214
+ // Generate models via generate-model CLI
215
+ const generateModelArgs = ["--type", dbType, "--output", modelsDir];
216
+ if (args.host) generateModelArgs.push("--host", args.host);
217
+ if (args.port) generateModelArgs.push("--port", args.port);
218
+ if (args.database) generateModelArgs.push("--database", args.database);
219
+ if (args.user) generateModelArgs.push("--user", args.user);
220
+ if (args.password) generateModelArgs.push("--password", args.password);
221
+ if (args.schema) generateModelArgs.push("--schema", args.schema);
222
+ if (args.env) generateModelArgs.push("--env", args.env);
223
+ if (args.tables) generateModelArgs.push("--tables", args.tables);
224
+
225
+ try {
226
+ execFileSync(
227
+ process.execPath,
228
+ [path.join(__dirname, "generate-model.js"), ...generateModelArgs],
229
+ { stdio: "inherit" },
230
+ );
231
+ } catch (err) {
232
+ console.error("Model generation failed.");
233
+ process.exit(1);
234
+ }
235
+
236
+ // Generate routes via generate-route CLI
237
+ const generateRouteArgs = ["--models", modelsDir, "--output", routesDir];
238
+ if (args.tables) generateRouteArgs.push("--tables", args.tables);
239
+
240
+ try {
241
+ execFileSync(
242
+ process.execPath,
243
+ [path.join(__dirname, "generate-route.js"), ...generateRouteArgs],
244
+ { stdio: "inherit" },
245
+ );
246
+ } catch (err) {
247
+ console.error("Route generation failed.");
248
+ process.exit(1);
249
+ }
250
+
251
+ // Write app.js
252
+ const appPath = path.join(outputDir, "app.js");
253
+ if (!fs.existsSync(appPath)) {
254
+ fs.writeFileSync(appPath, generateAppJs(dbType));
255
+ console.log(` Created ${appPath}`);
256
+ } else {
257
+ console.log(` Skipped ${appPath} (already exists)`);
258
+ }
259
+
260
+ // Write middleware/logger.js
261
+ const loggerPath = path.join(middlewareDir, "logger.js");
262
+ if (!fs.existsSync(loggerPath)) {
263
+ fs.writeFileSync(loggerPath, generateLoggerMiddleware());
264
+ console.log(` Created ${loggerPath}`);
265
+ }
266
+
267
+ // Write .env.example
268
+ const envPath = path.join(outputDir, ".env.example");
269
+ if (!fs.existsSync(envPath)) {
270
+ fs.writeFileSync(envPath, generateEnvExample(dbType));
271
+ console.log(` Created ${envPath}`);
272
+ }
273
+
274
+ // Write .gitignore
275
+ const gitignorePath = path.join(outputDir, ".gitignore");
276
+ if (!fs.existsSync(gitignorePath)) {
277
+ fs.writeFileSync(gitignorePath, generateGitignore());
278
+ console.log(` Created ${gitignorePath}`);
279
+ }
280
+
281
+ // Write placeholder files
282
+ const migrationReadme = path.join(migrationsDir, "README.md");
283
+ if (!fs.existsSync(migrationReadme)) {
284
+ fs.writeFileSync(
285
+ migrationReadme,
286
+ "# Migrations\n\nPlace your database migration scripts here.\n",
287
+ );
288
+ }
289
+ const sessionReadme = path.join(sessionsDir, "README.md");
290
+ if (!fs.existsSync(sessionReadme)) {
291
+ fs.writeFileSync(
292
+ sessionReadme,
293
+ "# Sessions\n\nSession configuration and store setup.\n",
294
+ );
295
+ }
296
+
297
+ console.log("\nApp scaffolded. To start:");
298
+ console.log(` cp .env.example .env`);
299
+ console.log(` npm install`);
300
+ console.log(` node app.js`);
301
+ }
302
+
303
+ function parseArgs(argv) {
304
+ const args = {};
305
+ for (let i = 0; i < argv.length; i++) {
306
+ const arg = argv[i];
307
+ if (arg.startsWith("--")) {
308
+ const key = arg.slice(2);
309
+ const next = argv[i + 1];
310
+ if (next && !next.startsWith("--")) {
311
+ args[key] = next;
312
+ i++;
313
+ } else {
314
+ args[key] = true;
315
+ }
316
+ }
317
+ }
318
+ return args;
319
+ }
320
+
321
+ function printUsage() {
322
+ console.log(`
323
+ Usage: rest-router-generate-app --type <db_type> [options]
324
+
325
+ Scaffolds a complete Express REST API app from an existing database.
326
+ Creates: app.js, models/, routes/, middleware/logger.js, migrations/, sessions/, .env.example
327
+
328
+ Options:
329
+ --type Database type (${SUPPORTED_TYPES.join(", ")}) [required]
330
+ --output Output directory (default: current directory)
331
+ --host Database host
332
+ --port Database port
333
+ --database Database name or file path
334
+ --user Database user
335
+ --password Database password
336
+ --schema Schema name (postgres only)
337
+ --tables Comma-separated tables (supports parent.child notation)
338
+ --env Path to .env file for DB connection
339
+ --help Show this help message
340
+
341
+ Examples:
342
+ rest-router-generate-app --type mysql --env .env
343
+ rest-router-generate-app --type sqlite3 --database ./myapp.db --output ./my-api
344
+ rest-router-generate-app --type postgres --env .env --tables users,posts,posts.comments
345
+ `);
346
+ }
347
+
348
+ if (require.main === module) {
349
+ main().catch((err) => {
350
+ console.error("Error:", err.message);
351
+ process.exit(1);
352
+ });
353
+ }
354
+
355
+ module.exports = {
356
+ generateAppJs,
357
+ generateLoggerMiddleware,
358
+ generateEnvExample,
359
+ };