navis.js 5.5.1 → 5.6.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 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.4.0
6
+ **Version:** 5.6.0
7
7
  **License:** MIT
8
8
 
9
9
  ## Philosophy
@@ -185,11 +185,18 @@ navis metrics
185
185
  - ✅ **GraphQL schema builder** - Schema definition helpers
186
186
  - ✅ **GraphQL middleware** - Easy integration with Navis.js routes
187
187
 
188
- ### v5.5 (Current)
188
+ ### v5.5
189
189
  - ✅ **Extended database adapters** - SQLite and SQL Server support
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 (Current)
194
+ - ✅ **Advanced query builders** - Fluent SQL query builder for all SQL databases
195
+ - ✅ **MongoDB query builder** - Fluent MongoDB query builder with aggregation support
196
+ - ✅ **Type-safe queries** - Full TypeScript support for query builders
197
+ - ✅ **Complex queries** - Support for JOINs, nested WHERE conditions, GROUP BY, HAVING, ORDER BY
198
+ - ✅ **Database-agnostic** - Automatic SQL dialect handling (PostgreSQL, MySQL, SQLite, SQL Server)
199
+
193
200
  ## API Reference
194
201
 
195
202
  ### NavisApp
@@ -617,6 +624,147 @@ app.get('/sqlite/users', async (req, res) => {
617
624
 
618
625
  See `examples/database-adapters-demo.js` and `examples/database-adapters-demo.ts` for complete examples.
619
626
 
627
+ ### Advanced Query Builders (v5.6)
628
+
629
+ **JavaScript Example:**
630
+ ```javascript
631
+ const { NavisApp, createPool, queryBuilder, mongoQueryBuilder, response } = require('navis.js');
632
+
633
+ const app = new NavisApp();
634
+
635
+ // SQL Query Builder
636
+ app.get('/users', async (req, res) => {
637
+ const db = createPool({
638
+ type: 'postgres',
639
+ connectionString: process.env.DATABASE_URL,
640
+ });
641
+
642
+ await db.connect();
643
+
644
+ // Fluent query builder
645
+ const users = await queryBuilder(db, 'users')
646
+ .select(['id', 'name', 'email'])
647
+ .where('status', '=', 'active')
648
+ .where('age', '>', 18)
649
+ .whereIn('role', ['user', 'admin'])
650
+ .orderBy('name', 'ASC')
651
+ .limit(10)
652
+ .execute();
653
+
654
+ await db.close();
655
+ response.success(res, { users });
656
+ });
657
+
658
+ // Complex WHERE with nested conditions
659
+ app.get('/products', async (req, res) => {
660
+ const db = createPool({ type: 'sqlite', connectionString: ':memory:' });
661
+ await db.connect();
662
+
663
+ const products = await queryBuilder(db, 'products')
664
+ .select('*')
665
+ .where((qb) => {
666
+ qb.where('category', '=', 'Electronics')
667
+ .orWhere('price', '<', 50);
668
+ })
669
+ .where('in_stock', '>', 0)
670
+ .groupBy('category')
671
+ .having('COUNT(*)', '>', 5)
672
+ .orderBy('price', 'DESC')
673
+ .execute();
674
+
675
+ await db.close();
676
+ response.success(res, { products });
677
+ });
678
+
679
+ // INSERT, UPDATE, DELETE
680
+ app.post('/users', async (req, res) => {
681
+ const db = createPool({ type: 'postgres', connectionString: process.env.DATABASE_URL });
682
+ await db.connect();
683
+
684
+ const result = await queryBuilder(db)
685
+ .insert('users', {
686
+ name: req.body.name,
687
+ email: req.body.email,
688
+ age: req.body.age,
689
+ })
690
+ .execute();
691
+
692
+ await db.close();
693
+ response.success(res, { id: result.insertId });
694
+ });
695
+
696
+ // MongoDB Query Builder
697
+ app.get('/mongo/users', async (req, res) => {
698
+ const db = createPool({
699
+ type: 'mongodb',
700
+ connectionString: process.env.MONGODB_URI,
701
+ });
702
+
703
+ await db.connect();
704
+
705
+ const users = await mongoQueryBuilder(db, 'users')
706
+ .where('status', 'active')
707
+ .gt('age', 18)
708
+ .in('role', ['user', 'admin'])
709
+ .sortDesc('created_at')
710
+ .limit(10)
711
+ .find();
712
+
713
+ await db.close();
714
+ response.success(res, { users });
715
+ });
716
+ ```
717
+
718
+ **TypeScript Example:**
719
+ ```typescript
720
+ import { NavisApp, createPool, queryBuilder, mongoQueryBuilder, response, DatabasePool, QueryBuilder } from 'navis.js';
721
+
722
+ const app = new NavisApp();
723
+
724
+ interface User {
725
+ id: number;
726
+ name: string;
727
+ email: string;
728
+ age: number;
729
+ }
730
+
731
+ app.get('/users', async (req, res) => {
732
+ const db: DatabasePool = createPool({
733
+ type: 'postgres',
734
+ connectionString: process.env.DATABASE_URL!,
735
+ });
736
+
737
+ await db.connect();
738
+
739
+ const users = await queryBuilder(db, 'users')
740
+ .select(['id', 'name', 'email'])
741
+ .where('status', '=', 'active')
742
+ .where((qb: QueryBuilder) => {
743
+ qb.where('age', '>', 18)
744
+ .orWhere('role', '=', 'admin');
745
+ })
746
+ .orderBy('name', 'ASC')
747
+ .limit(10)
748
+ .execute() as User[];
749
+
750
+ await db.close();
751
+ response.success(res, { users });
752
+ });
753
+ ```
754
+
755
+ **Query Builder Features:**
756
+ - ✅ **SELECT** - Fluent SELECT queries with WHERE, JOIN, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET
757
+ - ✅ **INSERT** - Type-safe INSERT operations
758
+ - ✅ **UPDATE** - UPDATE with WHERE conditions
759
+ - ✅ **DELETE** - DELETE with WHERE conditions
760
+ - ✅ **JOINs** - LEFT, RIGHT, INNER, FULL JOIN support
761
+ - ✅ **Nested conditions** - Complex WHERE with callbacks
762
+ - ✅ **Database-specific** - Automatic SQL dialect handling
763
+ - ✅ **MongoDB** - Full MongoDB query builder with aggregation pipeline
764
+ - ✅ **TypeScript** - Full type definitions and IntelliSense support
765
+
766
+ See `examples/query-builder-demo.js` and `examples/query-builder-demo.ts` for complete examples.
767
+
620
768
  ## Examples
621
769
 
622
770
  See the `examples/` directory:
@@ -637,6 +785,8 @@ See the `examples/` directory:
637
785
  - `graphql-demo.ts` - GraphQL server example with TypeScript types (v5.4) - TypeScript
638
786
  - `database-adapters-demo.js` - Extended database adapters example (v5.5) - JavaScript
639
787
  - `database-adapters-demo.ts` - Extended database adapters example (v5.5) - TypeScript
788
+ - `query-builder-demo.js` - Advanced query builder example (v5.6) - JavaScript
789
+ - `query-builder-demo.ts` - Advanced query builder example (v5.6) - TypeScript
640
790
  - `service-client-demo.js` - ServiceClient usage example
641
791
 
642
792
  ## Roadmap
@@ -668,7 +818,9 @@ TypeScript support: Full type definitions, type-safe API, IntelliSense
668
818
  ### v5.4 ✅
669
819
  GraphQL support: Lightweight GraphQL server, queries, mutations, resolvers, schema builder
670
820
 
671
- ### v5.5 ✅ (Current)
821
+ ### v5.5 ✅
822
+
823
+ ### v5.6 (Current)
672
824
  Extended database adapters: SQLite and SQL Server support, enhanced connection pooling
673
825
 
674
826
  ## What's Next?
@@ -676,8 +828,9 @@ Extended database adapters: SQLite and SQL Server support, enhanced connection p
676
828
  Future versions may include:
677
829
  - gRPC integration
678
830
  - Advanced caching strategies
679
- - Advanced query builders
680
831
  - Enhanced monitoring and alerting
832
+ - Database migrations
833
+ - ORM-like features
681
834
 
682
835
  ## Documentation
683
836
 
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Advanced Query Builder Demo - Navis.js
3
+ * Demonstrates fluent query builder for SQL and MongoDB (v5.6)
4
+ */
5
+
6
+ const { NavisApp, createPool, queryBuilder, mongoQueryBuilder, response } = require('../src/index');
7
+
8
+ const app = new NavisApp();
9
+
10
+ // ============================================
11
+ // SQL Query Builder Examples
12
+ // ============================================
13
+
14
+ // Example 1: SELECT with WHERE
15
+ app.get('/sql/select', async (req, res) => {
16
+ try {
17
+ const db = createPool({
18
+ type: 'sqlite',
19
+ connectionString: ':memory:',
20
+ });
21
+
22
+ await db.connect();
23
+
24
+ // Create table
25
+ await db.query(`
26
+ CREATE TABLE IF NOT EXISTS users (
27
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ name TEXT NOT NULL,
29
+ email TEXT NOT NULL,
30
+ age INTEGER,
31
+ status TEXT DEFAULT 'active'
32
+ )
33
+ `);
34
+
35
+ // Insert sample data
36
+ await db.query('INSERT INTO users (name, email, age, status) VALUES (?, ?, ?, ?)', ['Alice', 'alice@example.com', 30, 'active']);
37
+ await db.query('INSERT INTO users (name, email, age, status) VALUES (?, ?, ?, ?)', ['Bob', 'bob@example.com', 25, 'active']);
38
+ await db.query('INSERT INTO users (name, email, age, status) VALUES (?, ?, ?, ?)', ['Charlie', 'charlie@example.com', 35, 'inactive']);
39
+
40
+ // Query using query builder
41
+ const users = await queryBuilder(db, 'users')
42
+ .select(['id', 'name', 'email', 'age'])
43
+ .where('status', '=', 'active')
44
+ .where('age', '>', 20)
45
+ .orderBy('age', 'DESC')
46
+ .limit(10)
47
+ .execute();
48
+
49
+ await db.close();
50
+
51
+ response.success(res, {
52
+ method: 'SELECT with WHERE',
53
+ users,
54
+ });
55
+ } catch (error) {
56
+ response.error(res, `Error: ${error.message}`, 500);
57
+ }
58
+ });
59
+
60
+ // Example 2: Complex WHERE conditions
61
+ app.get('/sql/where-complex', async (req, res) => {
62
+ try {
63
+ const db = createPool({
64
+ type: 'sqlite',
65
+ connectionString: ':memory:',
66
+ });
67
+
68
+ await db.connect();
69
+
70
+ await db.query(`
71
+ CREATE TABLE IF NOT EXISTS products (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ name TEXT NOT NULL,
74
+ price REAL,
75
+ category TEXT,
76
+ in_stock INTEGER
77
+ )
78
+ `);
79
+
80
+ await db.query('INSERT INTO products (name, price, category, in_stock) VALUES (?, ?, ?, ?)', ['Laptop', 999.99, 'Electronics', 10]);
81
+ await db.query('INSERT INTO products (name, price, category, in_stock) VALUES (?, ?, ?, ?)', ['Phone', 599.99, 'Electronics', 5]);
82
+ await db.query('INSERT INTO products (name, price, category, in_stock) VALUES (?, ?, ?, ?)', ['Book', 19.99, 'Books', 0]);
83
+
84
+ // Complex WHERE with nested conditions
85
+ const products = await queryBuilder(db, 'products')
86
+ .select('*')
87
+ .where((qb) => {
88
+ qb.where('category', '=', 'Electronics')
89
+ .orWhere('price', '<', 50);
90
+ })
91
+ .where('in_stock', '>', 0)
92
+ .whereIn('category', ['Electronics', 'Books'])
93
+ .orderBy('price', 'ASC')
94
+ .execute();
95
+
96
+ await db.close();
97
+
98
+ response.success(res, {
99
+ method: 'Complex WHERE conditions',
100
+ products,
101
+ });
102
+ } catch (error) {
103
+ response.error(res, `Error: ${error.message}`, 500);
104
+ }
105
+ });
106
+
107
+ // Example 3: INSERT
108
+ app.get('/sql/insert', async (req, res) => {
109
+ try {
110
+ const db = createPool({
111
+ type: 'sqlite',
112
+ connectionString: ':memory:',
113
+ });
114
+
115
+ await db.connect();
116
+
117
+ await db.query(`
118
+ CREATE TABLE IF NOT EXISTS posts (
119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
120
+ title TEXT NOT NULL,
121
+ content TEXT,
122
+ author_id INTEGER,
123
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
124
+ )
125
+ `);
126
+
127
+ // Insert using query builder
128
+ const result = await queryBuilder(db)
129
+ .insert('posts', {
130
+ title: 'My First Post',
131
+ content: 'This is the content of my first post',
132
+ author_id: 1,
133
+ })
134
+ .execute();
135
+
136
+ const posts = await queryBuilder(db, 'posts').select('*').execute();
137
+
138
+ await db.close();
139
+
140
+ response.success(res, {
141
+ method: 'INSERT',
142
+ inserted: result,
143
+ posts,
144
+ });
145
+ } catch (error) {
146
+ response.error(res, `Error: ${error.message}`, 500);
147
+ }
148
+ });
149
+
150
+ // Example 4: UPDATE
151
+ app.get('/sql/update', async (req, res) => {
152
+ try {
153
+ const db = createPool({
154
+ type: 'sqlite',
155
+ connectionString: ':memory:',
156
+ });
157
+
158
+ await db.connect();
159
+
160
+ await db.query(`
161
+ CREATE TABLE IF NOT EXISTS users (
162
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
163
+ name TEXT NOT NULL,
164
+ email TEXT NOT NULL,
165
+ status TEXT DEFAULT 'active'
166
+ )
167
+ `);
168
+
169
+ await db.query('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
170
+
171
+ // Update using query builder
172
+ await queryBuilder(db)
173
+ .update('users', {
174
+ status: 'inactive',
175
+ })
176
+ .where('email', '=', 'john@example.com')
177
+ .execute();
178
+
179
+ const users = await queryBuilder(db, 'users').select('*').execute();
180
+
181
+ await db.close();
182
+
183
+ response.success(res, {
184
+ method: 'UPDATE',
185
+ users,
186
+ });
187
+ } catch (error) {
188
+ response.error(res, `Error: ${error.message}`, 500);
189
+ }
190
+ });
191
+
192
+ // Example 5: DELETE
193
+ app.get('/sql/delete', async (req, res) => {
194
+ try {
195
+ const db = createPool({
196
+ type: 'sqlite',
197
+ connectionString: ':memory:',
198
+ });
199
+
200
+ await db.connect();
201
+
202
+ await db.query(`
203
+ CREATE TABLE IF NOT EXISTS comments (
204
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
205
+ text TEXT NOT NULL,
206
+ user_id INTEGER
207
+ )
208
+ `);
209
+
210
+ await db.query('INSERT INTO comments (text, user_id) VALUES (?, ?)', ['Comment 1', 1]);
211
+ await db.query('INSERT INTO comments (text, user_id) VALUES (?, ?)', ['Comment 2', 2]);
212
+
213
+ // Delete using query builder
214
+ await queryBuilder(db)
215
+ .delete('comments')
216
+ .where('user_id', '=', 1)
217
+ .execute();
218
+
219
+ const comments = await queryBuilder(db, 'comments').select('*').execute();
220
+
221
+ await db.close();
222
+
223
+ response.success(res, {
224
+ method: 'DELETE',
225
+ comments,
226
+ });
227
+ } catch (error) {
228
+ response.error(res, `Error: ${error.message}`, 500);
229
+ }
230
+ });
231
+
232
+ // Example 6: JOIN
233
+ app.get('/sql/join', async (req, res) => {
234
+ try {
235
+ const db = createPool({
236
+ type: 'sqlite',
237
+ connectionString: ':memory:',
238
+ });
239
+
240
+ await db.connect();
241
+
242
+ await db.query(`
243
+ CREATE TABLE IF NOT EXISTS users (
244
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
245
+ name TEXT NOT NULL
246
+ )
247
+ `);
248
+
249
+ await db.query(`
250
+ CREATE TABLE IF NOT EXISTS orders (
251
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
252
+ user_id INTEGER,
253
+ total REAL,
254
+ FOREIGN KEY (user_id) REFERENCES users(id)
255
+ )
256
+ `);
257
+
258
+ await db.query('INSERT INTO users (name) VALUES (?)', ['Alice']);
259
+ await db.query('INSERT INTO orders (user_id, total) VALUES (?, ?)', [1, 99.99]);
260
+
261
+ // JOIN using query builder
262
+ const results = await queryBuilder(db)
263
+ .select(['users.name', 'orders.total'])
264
+ .from('users')
265
+ .leftJoin('orders', 'users.id', '=', 'orders.user_id')
266
+ .execute();
267
+
268
+ await db.close();
269
+
270
+ response.success(res, {
271
+ method: 'JOIN',
272
+ results,
273
+ });
274
+ } catch (error) {
275
+ response.error(res, `Error: ${error.message}`, 500);
276
+ }
277
+ });
278
+
279
+ // ============================================
280
+ // MongoDB Query Builder Examples
281
+ // ============================================
282
+
283
+ // Example 7: MongoDB FIND
284
+ app.get('/mongo/find', async (req, res) => {
285
+ try {
286
+ const db = createPool({
287
+ type: 'mongodb',
288
+ connectionString: process.env.MONGODB_URI || 'mongodb://localhost:27017/test',
289
+ });
290
+
291
+ await db.connect();
292
+
293
+ const collection = db.db.collection('users');
294
+
295
+ // Insert sample data
296
+ await collection.insertMany([
297
+ { name: 'Alice', age: 30, status: 'active' },
298
+ { name: 'Bob', age: 25, status: 'active' },
299
+ { name: 'Charlie', age: 35, status: 'inactive' },
300
+ ]);
301
+
302
+ // Query using MongoDB query builder
303
+ const users = await mongoQueryBuilder(db, 'users')
304
+ .where('status', 'active')
305
+ .gt('age', 20)
306
+ .sortDesc('age')
307
+ .limit(10)
308
+ .find();
309
+
310
+ await db.close();
311
+
312
+ response.success(res, {
313
+ method: 'MongoDB FIND',
314
+ users,
315
+ });
316
+ } catch (error) {
317
+ response.error(res, `Error: ${error.message}`, 500);
318
+ }
319
+ });
320
+
321
+ // Example 8: MongoDB INSERT
322
+ app.get('/mongo/insert', async (req, res) => {
323
+ try {
324
+ const db = createPool({
325
+ type: 'mongodb',
326
+ connectionString: process.env.MONGODB_URI || 'mongodb://localhost:27017/test',
327
+ });
328
+
329
+ await db.connect();
330
+
331
+ // Insert using MongoDB query builder
332
+ const result = await mongoQueryBuilder(db, 'posts')
333
+ .insert({
334
+ title: 'My First Post',
335
+ content: 'This is the content',
336
+ author: 'John',
337
+ })
338
+ .execute();
339
+
340
+ await db.close();
341
+
342
+ response.success(res, {
343
+ method: 'MongoDB INSERT',
344
+ result,
345
+ });
346
+ } catch (error) {
347
+ response.success(res, {
348
+ method: 'MongoDB INSERT',
349
+ note: 'MongoDB connection required. Install mongodb package and set MONGODB_URI environment variable.',
350
+ error: error.message,
351
+ });
352
+ }
353
+ });
354
+
355
+ // Health check
356
+ app.get('/health', (req, res) => {
357
+ response.success(res, {
358
+ status: 'ok',
359
+ features: [
360
+ 'SQL Query Builder (SELECT, INSERT, UPDATE, DELETE)',
361
+ 'MongoDB Query Builder (FIND, INSERT, UPDATE, DELETE)',
362
+ 'Complex WHERE conditions',
363
+ 'JOIN support',
364
+ 'Type-safe query building',
365
+ ],
366
+ });
367
+ });
368
+
369
+ // Start server
370
+ const PORT = process.env.PORT || 3000;
371
+ app.listen(PORT, () => {
372
+ console.log(`🚀 Navis.js Query Builder Demo running on http://localhost:${PORT}`);
373
+ console.log('\n📊 Available endpoints:');
374
+ console.log(` GET http://localhost:${PORT}/sql/select - SELECT with WHERE`);
375
+ console.log(` GET http://localhost:${PORT}/sql/where-complex - Complex WHERE conditions`);
376
+ console.log(` GET http://localhost:${PORT}/sql/insert - INSERT example`);
377
+ console.log(` GET http://localhost:${PORT}/sql/update - UPDATE example`);
378
+ console.log(` GET http://localhost:${PORT}/sql/delete - DELETE example`);
379
+ console.log(` GET http://localhost:${PORT}/sql/join - JOIN example`);
380
+ console.log(` GET http://localhost:${PORT}/mongo/find - MongoDB FIND`);
381
+ console.log(` GET http://localhost:${PORT}/mongo/insert - MongoDB INSERT`);
382
+ console.log(` GET http://localhost:${PORT}/health - Health check`);
383
+ console.log('\n💡 Note: SQLite examples use in-memory database (no setup required)');
384
+ console.log('💡 MongoDB examples require mongodb package and MONGODB_URI environment variable');
385
+ });
386
+