navis.js 5.6.0 → 5.7.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/README.md +13 -2
- package/examples/migrations/001_create_users_table.js +56 -0
- package/examples/migrations/002_create_posts_table.js +55 -0
- package/examples/migrations/003_create_comments_table.js +55 -0
- package/examples/orm-migrations-demo.js +319 -0
- package/examples/orm-migrations-demo.ts +309 -0
- package/package.json +1 -1
- package/src/db/migration.js +348 -0
- package/src/db/model.js +504 -0
- package/src/index.js +5 -0
- package/types/index.d.ts +196 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A lightweight, serverless-first, microservice API framework designed for AWS Lambda and Node.js.
|
|
4
4
|
|
|
5
5
|
**Author:** Syed Imran Ali
|
|
6
|
-
**Version:** 5.
|
|
6
|
+
**Version:** 5.7.0
|
|
7
7
|
**License:** MIT
|
|
8
8
|
|
|
9
9
|
## Philosophy
|
|
@@ -190,13 +190,21 @@ navis metrics
|
|
|
190
190
|
- ✅ **Enhanced database pool** - Support for 5 database types (PostgreSQL, MySQL, MongoDB, SQLite, SQL Server)
|
|
191
191
|
- ✅ **Improved connection handling** - Better error handling and connection management
|
|
192
192
|
|
|
193
|
-
### v5.6
|
|
193
|
+
### v5.6 ✅
|
|
194
194
|
- ✅ **Advanced query builders** - Fluent SQL query builder for all SQL databases
|
|
195
195
|
- ✅ **MongoDB query builder** - Fluent MongoDB query builder with aggregation support
|
|
196
196
|
- ✅ **Type-safe queries** - Full TypeScript support for query builders
|
|
197
197
|
- ✅ **Complex queries** - Support for JOINs, nested WHERE conditions, GROUP BY, HAVING, ORDER BY
|
|
198
198
|
- ✅ **Database-agnostic** - Automatic SQL dialect handling (PostgreSQL, MySQL, SQLite, SQL Server)
|
|
199
199
|
|
|
200
|
+
### v5.7 (Current)
|
|
201
|
+
- ✅ **ORM-like features** - Model definitions with relationships, hooks, and validation
|
|
202
|
+
- ✅ **Database migrations** - Migration system with up/down support and tracking
|
|
203
|
+
- ✅ **Model relationships** - hasMany, belongsTo, hasOne relationship definitions
|
|
204
|
+
- ✅ **Lifecycle hooks** - beforeSave, afterSave, beforeCreate, afterCreate, etc.
|
|
205
|
+
- ✅ **Change tracking** - isDirty, getChanged for detecting model modifications
|
|
206
|
+
- ✅ **TypeScript support** - Full type definitions for models and migrations
|
|
207
|
+
|
|
200
208
|
## API Reference
|
|
201
209
|
|
|
202
210
|
### NavisApp
|
|
@@ -835,6 +843,9 @@ Future versions may include:
|
|
|
835
843
|
## Documentation
|
|
836
844
|
|
|
837
845
|
- [V2 Features Guide](./docs/V2_FEATURES.md) - Complete v2 features documentation
|
|
846
|
+
- [V5.6 Features Guide](./docs/V5.6_FEATURES.md) - Advanced query builders documentation
|
|
847
|
+
- [V5.7 Features Guide](./docs/V5.7_FEATURES.md) - ORM-like features and migrations documentation
|
|
848
|
+
- [V5.7 Features Guide](./docs/V5.7_FEATURES.md) - ORM-like features and migrations documentation
|
|
838
849
|
- [V3 Features Guide](./docs/V3_FEATURES.md) - Complete v3 features documentation
|
|
839
850
|
- [V4 Features Guide](./docs/V4_FEATURES.md) - Complete v4 features documentation
|
|
840
851
|
- [V5 Features Guide](./docs/V5_FEATURES.md) - Complete v5 features documentation
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Create users table
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
async up(dbPool) {
|
|
7
|
+
const dbType = dbPool.type.toLowerCase();
|
|
8
|
+
|
|
9
|
+
if (dbType === 'mongodb') {
|
|
10
|
+
// MongoDB: Create collection (collections are created automatically)
|
|
11
|
+
const collection = dbPool.db.collection('users');
|
|
12
|
+
await collection.createIndex({ email: 1 }, { unique: true });
|
|
13
|
+
} else {
|
|
14
|
+
// SQL: Create table
|
|
15
|
+
const sql = `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
name TEXT NOT NULL,
|
|
19
|
+
email TEXT NOT NULL UNIQUE,
|
|
20
|
+
age INTEGER,
|
|
21
|
+
status TEXT DEFAULT 'active',
|
|
22
|
+
created_at DATETIME,
|
|
23
|
+
updated_at DATETIME
|
|
24
|
+
)
|
|
25
|
+
`.replace(/AUTOINCREMENT/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
26
|
+
? 'SERIAL'
|
|
27
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
28
|
+
? 'AUTO_INCREMENT'
|
|
29
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
30
|
+
? 'IDENTITY(1,1)'
|
|
31
|
+
: 'AUTOINCREMENT'
|
|
32
|
+
).replace(/DATETIME/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
33
|
+
? 'TIMESTAMP'
|
|
34
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
35
|
+
? 'DATETIME'
|
|
36
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
37
|
+
? 'DATETIME2'
|
|
38
|
+
: 'DATETIME'
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
await dbPool.query(sql);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async down(dbPool) {
|
|
46
|
+
const dbType = dbPool.type.toLowerCase();
|
|
47
|
+
|
|
48
|
+
if (dbType === 'mongodb') {
|
|
49
|
+
const collection = dbPool.db.collection('users');
|
|
50
|
+
await collection.drop();
|
|
51
|
+
} else {
|
|
52
|
+
await dbPool.query('DROP TABLE IF EXISTS users');
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Create posts table
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
async up(dbPool) {
|
|
7
|
+
const dbType = dbPool.type.toLowerCase();
|
|
8
|
+
|
|
9
|
+
if (dbType === 'mongodb') {
|
|
10
|
+
const collection = dbPool.db.collection('posts');
|
|
11
|
+
await collection.createIndex({ user_id: 1 });
|
|
12
|
+
await collection.createIndex({ created_at: -1 });
|
|
13
|
+
} else {
|
|
14
|
+
const sql = `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS posts (
|
|
16
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17
|
+
title TEXT NOT NULL,
|
|
18
|
+
content TEXT,
|
|
19
|
+
user_id INTEGER NOT NULL,
|
|
20
|
+
created_at DATETIME,
|
|
21
|
+
updated_at DATETIME,
|
|
22
|
+
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
23
|
+
)
|
|
24
|
+
`.replace(/AUTOINCREMENT/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
25
|
+
? 'SERIAL'
|
|
26
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
27
|
+
? 'AUTO_INCREMENT'
|
|
28
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
29
|
+
? 'IDENTITY(1,1)'
|
|
30
|
+
: 'AUTOINCREMENT'
|
|
31
|
+
).replace(/DATETIME/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
32
|
+
? 'TIMESTAMP'
|
|
33
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
34
|
+
? 'DATETIME'
|
|
35
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
36
|
+
? 'DATETIME2'
|
|
37
|
+
: 'DATETIME'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
await dbPool.query(sql);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async down(dbPool) {
|
|
45
|
+
const dbType = dbPool.type.toLowerCase();
|
|
46
|
+
|
|
47
|
+
if (dbType === 'mongodb') {
|
|
48
|
+
const collection = dbPool.db.collection('posts');
|
|
49
|
+
await collection.drop();
|
|
50
|
+
} else {
|
|
51
|
+
await dbPool.query('DROP TABLE IF EXISTS posts');
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Create comments table
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
async up(dbPool) {
|
|
7
|
+
const dbType = dbPool.type.toLowerCase();
|
|
8
|
+
|
|
9
|
+
if (dbType === 'mongodb') {
|
|
10
|
+
const collection = dbPool.db.collection('comments');
|
|
11
|
+
await collection.createIndex({ post_id: 1 });
|
|
12
|
+
await collection.createIndex({ user_id: 1 });
|
|
13
|
+
} else {
|
|
14
|
+
const sql = `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS comments (
|
|
16
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17
|
+
text TEXT NOT NULL,
|
|
18
|
+
post_id INTEGER NOT NULL,
|
|
19
|
+
user_id INTEGER NOT NULL,
|
|
20
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
21
|
+
FOREIGN KEY (post_id) REFERENCES posts(id),
|
|
22
|
+
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
23
|
+
)
|
|
24
|
+
`.replace(/AUTOINCREMENT/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
25
|
+
? 'SERIAL'
|
|
26
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
27
|
+
? 'AUTO_INCREMENT'
|
|
28
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
29
|
+
? 'IDENTITY(1,1)'
|
|
30
|
+
: 'AUTOINCREMENT'
|
|
31
|
+
).replace(/DATETIME/g, dbType === 'postgres' || dbType === 'postgresql'
|
|
32
|
+
? 'TIMESTAMP'
|
|
33
|
+
: dbType === 'mysql' || dbType === 'mariadb'
|
|
34
|
+
? 'DATETIME'
|
|
35
|
+
: dbType === 'mssql' || dbType === 'sqlserver'
|
|
36
|
+
? 'DATETIME2'
|
|
37
|
+
: 'DATETIME'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
await dbPool.query(sql);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async down(dbPool) {
|
|
45
|
+
const dbType = dbPool.type.toLowerCase();
|
|
46
|
+
|
|
47
|
+
if (dbType === 'mongodb') {
|
|
48
|
+
const collection = dbPool.db.collection('comments');
|
|
49
|
+
await collection.drop();
|
|
50
|
+
} else {
|
|
51
|
+
await dbPool.query('DROP TABLE IF EXISTS comments');
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORM-like Features and Migrations Demo - Navis.js
|
|
3
|
+
* Demonstrates Model definitions and database migrations (v5.7)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { NavisApp, createPool, Model, createMigration, response } = require('../src/index');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const app = new NavisApp();
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// Define Models
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
class User extends Model {
|
|
16
|
+
static get tableName() {
|
|
17
|
+
return 'users';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static get primaryKey() {
|
|
21
|
+
return 'id';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async validate() {
|
|
25
|
+
if (!this.email || !this.email.includes('@')) {
|
|
26
|
+
throw new Error('Invalid email address');
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async beforeSave() {
|
|
32
|
+
if (this._isNew) {
|
|
33
|
+
this.created_at = new Date();
|
|
34
|
+
}
|
|
35
|
+
this.updated_at = new Date();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class Post extends Model {
|
|
40
|
+
static get tableName() {
|
|
41
|
+
return 'posts';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static get primaryKey() {
|
|
45
|
+
return 'id';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async beforeSave() {
|
|
49
|
+
if (this._isNew) {
|
|
50
|
+
this.created_at = new Date();
|
|
51
|
+
}
|
|
52
|
+
this.updated_at = new Date();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class Comment extends Model {
|
|
57
|
+
static get tableName() {
|
|
58
|
+
return 'comments';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static get primaryKey() {
|
|
62
|
+
return 'id';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Define relationships
|
|
67
|
+
Post.belongsTo('author', User, 'user_id');
|
|
68
|
+
User.hasMany('posts', Post, 'user_id');
|
|
69
|
+
Post.hasMany('comments', Comment, 'post_id');
|
|
70
|
+
Comment.belongsTo('post', Post, 'post_id');
|
|
71
|
+
Comment.belongsTo('author', User, 'user_id');
|
|
72
|
+
|
|
73
|
+
// ============================================
|
|
74
|
+
// API Routes
|
|
75
|
+
// ============================================
|
|
76
|
+
|
|
77
|
+
// Initialize database
|
|
78
|
+
let db = null;
|
|
79
|
+
|
|
80
|
+
app.get('/init', async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
db = createPool({
|
|
83
|
+
type: 'sqlite',
|
|
84
|
+
connectionString: ':memory:',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await db.connect();
|
|
88
|
+
|
|
89
|
+
// Set database for models
|
|
90
|
+
User.setDatabase(db);
|
|
91
|
+
Post.setDatabase(db);
|
|
92
|
+
Comment.setDatabase(db);
|
|
93
|
+
|
|
94
|
+
// Run migrations
|
|
95
|
+
const migration = createMigration(db, path.join(__dirname, 'migrations'));
|
|
96
|
+
await migration.init();
|
|
97
|
+
const result = await migration.up();
|
|
98
|
+
|
|
99
|
+
response.success(res, {
|
|
100
|
+
message: 'Database initialized',
|
|
101
|
+
migrations: result,
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
response.error(res, `Init error: ${error.message}`, 500);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Create user
|
|
109
|
+
app.post('/users', async (req, res) => {
|
|
110
|
+
try {
|
|
111
|
+
const user = await User.create({
|
|
112
|
+
name: req.body.name || 'John Doe',
|
|
113
|
+
email: req.body.email || 'john@example.com',
|
|
114
|
+
age: req.body.age || 30,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
response.success(res, {
|
|
118
|
+
message: 'User created',
|
|
119
|
+
user: user.toJSON(),
|
|
120
|
+
}, 201);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
response.error(res, `Error: ${error.message}`, 400);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Get all users
|
|
127
|
+
app.get('/users', async (req, res) => {
|
|
128
|
+
try {
|
|
129
|
+
const users = await User.find({}, {
|
|
130
|
+
orderBy: 'name',
|
|
131
|
+
orderDirection: 'ASC',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
response.success(res, {
|
|
135
|
+
users: users.map(u => u.toJSON()),
|
|
136
|
+
});
|
|
137
|
+
} catch (error) {
|
|
138
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Get user by ID
|
|
143
|
+
app.get('/users/:id', async (req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const user = await User.findById(req.params.id);
|
|
146
|
+
|
|
147
|
+
if (!user) {
|
|
148
|
+
return response.error(res, 'User not found', 404);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
response.success(res, {
|
|
152
|
+
user: user.toJSON(),
|
|
153
|
+
});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Update user
|
|
160
|
+
app.put('/users/:id', async (req, res) => {
|
|
161
|
+
try {
|
|
162
|
+
const user = await User.findById(req.params.id);
|
|
163
|
+
|
|
164
|
+
if (!user) {
|
|
165
|
+
return response.error(res, 'User not found', 404);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Update fields
|
|
169
|
+
if (req.body.name) user.name = req.body.name;
|
|
170
|
+
if (req.body.email) user.email = req.body.email;
|
|
171
|
+
if (req.body.age) user.age = req.body.age;
|
|
172
|
+
|
|
173
|
+
await user.save();
|
|
174
|
+
|
|
175
|
+
response.success(res, {
|
|
176
|
+
message: 'User updated',
|
|
177
|
+
user: user.toJSON(),
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
response.error(res, `Error: ${error.message}`, 400);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Delete user
|
|
185
|
+
app.delete('/users/:id', async (req, res) => {
|
|
186
|
+
try {
|
|
187
|
+
const user = await User.findById(req.params.id);
|
|
188
|
+
|
|
189
|
+
if (!user) {
|
|
190
|
+
return response.error(res, 'User not found', 404);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await user.delete();
|
|
194
|
+
|
|
195
|
+
response.success(res, {
|
|
196
|
+
message: 'User deleted',
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Create post
|
|
204
|
+
app.post('/posts', async (req, res) => {
|
|
205
|
+
try {
|
|
206
|
+
const post = await Post.create({
|
|
207
|
+
title: req.body.title || 'My First Post',
|
|
208
|
+
content: req.body.content || 'This is the content',
|
|
209
|
+
user_id: req.body.user_id || 1,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
response.success(res, {
|
|
213
|
+
message: 'Post created',
|
|
214
|
+
post: post.toJSON(),
|
|
215
|
+
}, 201);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
response.error(res, `Error: ${error.message}`, 400);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Get posts with author
|
|
222
|
+
app.get('/posts', async (req, res) => {
|
|
223
|
+
try {
|
|
224
|
+
const posts = await Post.find({}, {
|
|
225
|
+
orderBy: 'created_at',
|
|
226
|
+
orderDirection: 'DESC',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Load relationships
|
|
230
|
+
const postsWithAuthor = await Promise.all(
|
|
231
|
+
posts.map(async (post) => {
|
|
232
|
+
const postData = post.toJSON();
|
|
233
|
+
const author = await post.author;
|
|
234
|
+
return {
|
|
235
|
+
...postData,
|
|
236
|
+
author: author ? author.toJSON() : null,
|
|
237
|
+
};
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
response.success(res, {
|
|
242
|
+
posts: postsWithAuthor,
|
|
243
|
+
});
|
|
244
|
+
} catch (error) {
|
|
245
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Get user with posts (relationship)
|
|
250
|
+
app.get('/users/:id/posts', async (req, res) => {
|
|
251
|
+
try {
|
|
252
|
+
const user = await User.findById(req.params.id);
|
|
253
|
+
|
|
254
|
+
if (!user) {
|
|
255
|
+
return response.error(res, 'User not found', 404);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const posts = await user.posts;
|
|
259
|
+
|
|
260
|
+
response.success(res, {
|
|
261
|
+
user: user.toJSON(),
|
|
262
|
+
posts: posts.map(p => p.toJSON()),
|
|
263
|
+
});
|
|
264
|
+
} catch (error) {
|
|
265
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Migration status
|
|
270
|
+
app.get('/migrations/status', async (req, res) => {
|
|
271
|
+
try {
|
|
272
|
+
if (!db) {
|
|
273
|
+
return response.error(res, 'Database not initialized', 400);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const migration = createMigration(db, path.join(__dirname, 'migrations'));
|
|
277
|
+
const status = await migration.status();
|
|
278
|
+
|
|
279
|
+
response.success(res, status);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
response.error(res, `Error: ${error.message}`, 500);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Health check
|
|
286
|
+
app.get('/health', (req, res) => {
|
|
287
|
+
response.success(res, {
|
|
288
|
+
status: 'ok',
|
|
289
|
+
features: [
|
|
290
|
+
'ORM-like Model definitions',
|
|
291
|
+
'Model relationships (hasMany, belongsTo, hasOne)',
|
|
292
|
+
'Model hooks (beforeSave, afterSave, etc.)',
|
|
293
|
+
'Model validation',
|
|
294
|
+
'Database migrations',
|
|
295
|
+
'Migration up/down',
|
|
296
|
+
'Migration tracking',
|
|
297
|
+
],
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Start server
|
|
302
|
+
const PORT = process.env.PORT || 3000;
|
|
303
|
+
app.listen(PORT, () => {
|
|
304
|
+
console.log(`🚀 Navis.js ORM & Migrations Demo running on http://localhost:${PORT}`);
|
|
305
|
+
console.log('\n📊 Available endpoints:');
|
|
306
|
+
console.log(` GET http://localhost:${PORT}/init - Initialize database and run migrations`);
|
|
307
|
+
console.log(` POST http://localhost:${PORT}/users - Create user`);
|
|
308
|
+
console.log(` GET http://localhost:${PORT}/users - Get all users`);
|
|
309
|
+
console.log(` GET http://localhost:${PORT}/users/:id - Get user by ID`);
|
|
310
|
+
console.log(` PUT http://localhost:${PORT}/users/:id - Update user`);
|
|
311
|
+
console.log(` DELETE http://localhost:${PORT}/users/:id - Delete user`);
|
|
312
|
+
console.log(` POST http://localhost:${PORT}/posts - Create post`);
|
|
313
|
+
console.log(` GET http://localhost:${PORT}/posts - Get all posts with authors`);
|
|
314
|
+
console.log(` GET http://localhost:${PORT}/users/:id/posts - Get user's posts`);
|
|
315
|
+
console.log(` GET http://localhost:${PORT}/migrations/status - Get migration status`);
|
|
316
|
+
console.log(` GET http://localhost:${PORT}/health - Health check`);
|
|
317
|
+
console.log('\n💡 Note: Call /init first to set up the database');
|
|
318
|
+
});
|
|
319
|
+
|