@arikajs/database 0.0.3 → 0.0.5

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.
Files changed (92) hide show
  1. package/README.md +392 -25
  2. package/dist/Connections/MongoDBConnection.d.ts +81 -0
  3. package/dist/Connections/MongoDBConnection.d.ts.map +1 -0
  4. package/dist/Connections/MongoDBConnection.js +203 -0
  5. package/dist/Connections/MongoDBConnection.js.map +1 -0
  6. package/dist/Connections/MySQLConnection.d.ts +1 -0
  7. package/dist/Connections/MySQLConnection.d.ts.map +1 -1
  8. package/dist/Connections/MySQLConnection.js +47 -10
  9. package/dist/Connections/MySQLConnection.js.map +1 -1
  10. package/dist/Connections/PostgreSQLConnection.d.ts +1 -0
  11. package/dist/Connections/PostgreSQLConnection.d.ts.map +1 -1
  12. package/dist/Connections/PostgreSQLConnection.js +43 -9
  13. package/dist/Connections/PostgreSQLConnection.js.map +1 -1
  14. package/dist/Connections/SQLiteConnection.d.ts +1 -0
  15. package/dist/Connections/SQLiteConnection.d.ts.map +1 -1
  16. package/dist/Connections/SQLiteConnection.js +38 -7
  17. package/dist/Connections/SQLiteConnection.js.map +1 -1
  18. package/dist/Contracts/Database.d.ts +71 -4
  19. package/dist/Contracts/Database.d.ts.map +1 -1
  20. package/dist/Contracts/Schema.d.ts +4 -0
  21. package/dist/Contracts/Schema.d.ts.map +1 -1
  22. package/dist/Database.d.ts +30 -3
  23. package/dist/Database.d.ts.map +1 -1
  24. package/dist/Database.js +39 -1
  25. package/dist/Database.js.map +1 -1
  26. package/dist/DatabaseManager.d.ts +17 -3
  27. package/dist/DatabaseManager.d.ts.map +1 -1
  28. package/dist/DatabaseManager.js +27 -11
  29. package/dist/DatabaseManager.js.map +1 -1
  30. package/dist/Migrations/Migrator.d.ts.map +1 -1
  31. package/dist/Migrations/Migrator.js +35 -3
  32. package/dist/Migrations/Migrator.js.map +1 -1
  33. package/dist/Model/GlobalScope.d.ts +44 -0
  34. package/dist/Model/GlobalScope.d.ts.map +1 -0
  35. package/dist/Model/GlobalScope.js +64 -0
  36. package/dist/Model/GlobalScope.js.map +1 -0
  37. package/dist/Model/Model.d.ts +168 -4
  38. package/dist/Model/Model.d.ts.map +1 -1
  39. package/dist/Model/Model.js +476 -16
  40. package/dist/Model/Model.js.map +1 -1
  41. package/dist/Model/Observer.d.ts +39 -0
  42. package/dist/Model/Observer.d.ts.map +1 -0
  43. package/dist/Model/Observer.js +48 -0
  44. package/dist/Model/Observer.js.map +1 -0
  45. package/dist/Model/Relations.d.ts +201 -10
  46. package/dist/Model/Relations.d.ts.map +1 -1
  47. package/dist/Model/Relations.js +472 -27
  48. package/dist/Model/Relations.js.map +1 -1
  49. package/dist/Query/Expression.d.ts +16 -0
  50. package/dist/Query/Expression.d.ts.map +1 -0
  51. package/dist/Query/Expression.js +25 -0
  52. package/dist/Query/Expression.js.map +1 -0
  53. package/dist/Query/QueryBuilder.d.ts +64 -6
  54. package/dist/Query/QueryBuilder.d.ts.map +1 -1
  55. package/dist/Query/QueryBuilder.js +234 -15
  56. package/dist/Query/QueryBuilder.js.map +1 -1
  57. package/dist/Query/QueryLogger.d.ts +55 -0
  58. package/dist/Query/QueryLogger.d.ts.map +1 -0
  59. package/dist/Query/QueryLogger.js +82 -0
  60. package/dist/Query/QueryLogger.js.map +1 -0
  61. package/dist/Schema/Grammars/Grammar.d.ts +5 -0
  62. package/dist/Schema/Grammars/Grammar.d.ts.map +1 -1
  63. package/dist/Schema/Grammars/Grammar.js.map +1 -1
  64. package/dist/Schema/Grammars/MySQLGrammar.d.ts +1 -0
  65. package/dist/Schema/Grammars/MySQLGrammar.d.ts.map +1 -1
  66. package/dist/Schema/Grammars/MySQLGrammar.js +42 -0
  67. package/dist/Schema/Grammars/MySQLGrammar.js.map +1 -1
  68. package/dist/Schema/Grammars/PostgreSQLGrammar.d.ts +1 -0
  69. package/dist/Schema/Grammars/PostgreSQLGrammar.d.ts.map +1 -1
  70. package/dist/Schema/Grammars/PostgreSQLGrammar.js +46 -0
  71. package/dist/Schema/Grammars/PostgreSQLGrammar.js.map +1 -1
  72. package/dist/Schema/Grammars/SQLiteGrammar.d.ts +1 -0
  73. package/dist/Schema/Grammars/SQLiteGrammar.d.ts.map +1 -1
  74. package/dist/Schema/Grammars/SQLiteGrammar.js +31 -0
  75. package/dist/Schema/Grammars/SQLiteGrammar.js.map +1 -1
  76. package/dist/Schema/Schema.d.ts +6 -0
  77. package/dist/Schema/Schema.d.ts.map +1 -1
  78. package/dist/Schema/Schema.js +10 -0
  79. package/dist/Schema/Schema.js.map +1 -1
  80. package/dist/Schema/SchemaBuilder.d.ts +4 -0
  81. package/dist/Schema/SchemaBuilder.d.ts.map +1 -1
  82. package/dist/Schema/SchemaBuilder.js +15 -0
  83. package/dist/Schema/SchemaBuilder.js.map +1 -1
  84. package/dist/Transactions/TransactionManager.d.ts +28 -0
  85. package/dist/Transactions/TransactionManager.d.ts.map +1 -0
  86. package/dist/Transactions/TransactionManager.js +68 -0
  87. package/dist/Transactions/TransactionManager.js.map +1 -0
  88. package/dist/index.d.ts +11 -1
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +21 -1
  91. package/dist/index.js.map +1 -1
  92. package/package.json +10 -6
package/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # @arikajs/database
2
2
 
3
+ <div align="center">
4
+
5
+ <img src="../cli/templates/app/public/assets/img/logo.png" alt="ArikaJS Logo" width="400">
6
+
7
+ </div>
8
+
3
9
  **@arikajs/database** is the official database layer for the ArikaJS framework.
4
- It provides a powerful, extensible, and framework-integrated database system inspired by Laravel's Eloquent & Query Builder — but designed natively for **Node.js & TypeScript**.
10
+ It provides a powerful, extensible, and framework-integrated database system with an elegant, fluent API — but designed natively for **Node.js & TypeScript**.
5
11
 
6
12
  This package powers **DB facade**, **Models**, **Migrations**, and **Query Builder** across all ArikaJS applications.
7
13
 
@@ -11,7 +17,7 @@ This package powers **DB facade**, **Models**, **Migrations**, and **Query Build
11
17
 
12
18
  ### ✅ Available (v0.x)
13
19
  - ✅ Multiple database connections
14
- - ✅ MySQL, PostgreSQL & SQLite support
20
+ - ✅ MySQL, PostgreSQL, SQLite, & MongoDB support
15
21
  - ✅ Fluent Query Builder
16
22
  - ✅ Static Model querying
17
23
  - ✅ Instance-style Models (Active Record)
@@ -25,6 +31,15 @@ This package powers **DB facade**, **Models**, **Migrations**, and **Query Build
25
31
  - ✅ Framework-integrated configuration
26
32
  - ✅ TypeScript-first design
27
33
  - ✅ Works with native ArikaJS DI container
34
+ - ✅ Query result caching
35
+ - ✅ Query Builder & Model Pagination
36
+ - ✅ Attribute Casting & Magic Accessors/Mutators
37
+ - ✅ Bulk Operations (Bulk Insert)
38
+ - ✅ Connection Pooling & Read/Write Splitting
39
+ - ✅ Transactions (with nested savepoints)
40
+ - ✅ Model Observers (lifecycle hooks)
41
+ - ✅ Query Logging & Debug Mode
42
+ - ✅ Global Scopes
28
43
 
29
44
  ---
30
45
 
@@ -35,6 +50,7 @@ This package powers **DB facade**, **Models**, **Migrations**, and **Query Build
35
50
  | MySQL | ✅ Supported |
36
51
  | PostgreSQL | ✅ Supported |
37
52
  | SQLite | ✅ Supported |
53
+ | MongoDB | ✅ Supported |
38
54
 
39
55
  ---
40
56
 
@@ -79,6 +95,15 @@ export default {
79
95
  driver: 'sqlite',
80
96
  database: env('DB_DATABASE', 'database.sqlite'),
81
97
  },
98
+
99
+ mongodb: {
100
+ driver: 'mongodb',
101
+ host: env('DB_HOST', '127.0.0.1'),
102
+ port: env('DB_PORT', 27017),
103
+ database: env('DB_DATABASE', 'arikajs'),
104
+ username: env('DB_USERNAME', ''),
105
+ password: env('DB_PASSWORD', ''),
106
+ },
82
107
  },
83
108
  };
84
109
  ```
@@ -139,6 +164,34 @@ console.log(user.isDirty()); // true
139
164
  console.log(user.getDirty()); // { name: 'New Name' }
140
165
  ```
141
166
 
167
+ ### 🪄 Attribute Casting & Magic Accessors/Mutators
168
+ ArikaJS Model system supports defining exact types your database inputs should cast to. Using a Proxy behind the scenes, you can retrieve any attribute implicitly and add overrides (mutators/accessors).
169
+ ```typescript
170
+ class User extends Model {
171
+ // Instruct ArikaJS to cast these properties when saving/retrieving
172
+ protected casts = {
173
+ is_active: 'boolean',
174
+ metadata: 'json',
175
+ last_login: 'datetime'
176
+ };
177
+
178
+ // Mutator: Set Password Hash
179
+ setPasswordAttribute(value: string) {
180
+ this.attributes['password'] = hash(value);
181
+ }
182
+
183
+ // Accessor: Get Full Name
184
+ getFullNameAttribute() {
185
+ return `${this.first_name} ${this.last_name}`;
186
+ }
187
+ }
188
+
189
+ const user = new User();
190
+ user.is_active = true; // Implicit proxy saving!
191
+ user.password = 'my_secure_pass'; // Triggers setPasswordAttribute!
192
+ console.log(user.full_name); // Magic accessor triggered!
193
+ ```
194
+
142
195
  ### 🔗 Relationships
143
196
 
144
197
  ```typescript
@@ -232,35 +285,82 @@ await User.restore(1);
232
285
  await User.forceDelete(1);
233
286
  ```
234
287
 
235
- ### 🧱 Migrations (via CLI – planned)
288
+ ### 🧱 Schema Builder & Migrations
236
289
 
237
- ```bash
238
- arika make:migration create_users_table
239
- arika migrate
290
+ ArikaJS offers a fluent schema builder for creating and altering tables.
291
+
292
+ ```typescript
293
+ import { Schema } from '@arikajs/database';
294
+
295
+ // 1. Create a table
296
+ await Schema.create('users', (table) => {
297
+ table.id();
298
+ table.string('name');
299
+ table.string('email').unique();
300
+ table.boolean('is_active').default(true);
301
+ table.timestamps();
302
+ });
303
+
304
+ // 2. Alter an existing table
305
+ await Schema.table('users', (table) => {
306
+ table.string('phone_number').nullable(); // Adds phone_number
307
+ table.dropColumn('is_active'); // Drops a column
308
+ table.index(['name', 'email']); // Adds a compound index
309
+ });
310
+
311
+ // 3. Drop tables
312
+ await Schema.dropIfExists('users');
240
313
  ```
241
314
 
315
+ (Note: A CLI tool to automatically generate and run these migrations is planned: `arika make:migration create_users_table`).
316
+
242
317
  ---
243
318
 
244
- ## 🧩 Architecture Overview
319
+ ## 🏗 Architecture
245
320
 
246
- ```
321
+ ```text
247
322
  database/
248
323
  ├── src/
249
- │ ├── Connections/
250
- │ │ ├── MySQLConnection
251
- │ │ ├── PostgreSQLConnection
252
- │ │ └── SQLiteConnection
253
- ├── Model/
254
- ├── Model
255
- │ │ ├── Relations
256
- │ │ └── SoftDeletes
257
- │ ├── QueryBuilder
324
+ │ ├── Connections
325
+ │ │ ├── MongoDBConnection.ts
326
+ │ │ ├── MySQLConnection.ts
327
+ │ │ ├── PostgreSQLConnection.ts
328
+ │ └── SQLiteConnection.ts
329
+ │ ├── Contracts
330
+ │ │ ├── Database.ts
331
+ │ │ └── Schema.ts
332
+ │ ├── Migrations
333
+ │ │ ├── Migration.ts
334
+ │ │ └── Migrator.ts
335
+ │ ├── Model
336
+ │ │ ├── GlobalScope.ts
337
+ │ │ ├── Model.ts
338
+ │ │ ├── Observer.ts
339
+ │ │ ├── Relations.ts
340
+ │ │ └── SoftDeletes.ts
341
+ │ ├── Query
342
+ │ │ ├── Expression.ts
343
+ │ │ ├── QueryBuilder.ts
344
+ │ │ └── QueryLogger.ts
258
345
  │ ├── Schema
259
- │ ├── DatabaseManager
260
- └── Facades/
261
- └── DB
262
- ├── config/
263
- │ └── database.ts
346
+ ├── Grammars
347
+ │ │ ├── Grammar.ts
348
+ │ │ ├── MySQLGrammar.ts
349
+ │ │ │ ├── PostgreSQLGrammar.ts
350
+ │ │ └── SQLiteGrammar.ts
351
+ │ │ ├── Schema.ts
352
+ │ │ └── SchemaBuilder.ts
353
+ │ ├── Seeders
354
+ │ │ ├── Seeder.ts
355
+ │ │ └── SeedRunner.ts
356
+ │ ├── Transactions
357
+ │ │ └── TransactionManager.ts
358
+ │ ├── Database.ts
359
+ │ ├── DatabaseManager.ts
360
+ │ └── index.ts
361
+ ├── tests/
362
+ ├── package.json
363
+ ├── tsconfig.json
264
364
  └── README.md
265
365
  ```
266
366
 
@@ -285,14 +385,281 @@ class User extends Model {
285
385
  }
286
386
  ```
287
387
 
288
- ### ⚡ Query Result Caching
388
+ ### ⚡ Query Result Caching (Inline Caching)
389
+ Caching is natively supported inline using the `.cache()` builder method. It integrates directly with `@arikajs/cache` when registered inside the framework.
289
390
  ```typescript
290
391
  const users = await User.where('active', true)
291
392
  .cache(3600) // Cache for 1 hour
292
393
  .get();
293
394
  ```
294
395
 
295
- ### 🔍 Advanced Query Features
396
+ ### 📄 Pagination
397
+ Quickly retrieve paginated models using `.paginate()`. It directly queries out metadata alongside the specific models.
398
+ ```typescript
399
+ const { data, meta } = await User.where('status', 'active').paginate(1, 15);
400
+
401
+ // Output:
402
+ // {
403
+ // data: [...], // Array of 15 User model instances
404
+ // meta: {
405
+ // total: 50,
406
+ // per_page: 15,
407
+ // current_page: 1,
408
+ // last_page: 4,
409
+ // first_page: 1
410
+ // }
411
+ // }
412
+ ```
413
+
414
+ ### 🏁 Bulk Operations
415
+ Executing a bulk operation gives you massive optimization boosts across a large SaaS platform. ArikaJS simplifies doing arrays of records sequentially.
416
+ ```typescript
417
+ await User.insert([
418
+ { first_name: 'John', last_name: 'Doe' },
419
+ { first_name: 'Jane', last_name: 'Smith' },
420
+ { first_name: 'William', last_name: 'James' }
421
+ ]);
422
+ ```
423
+
424
+ ### 🔀 Connection Pooling & Read/Write Splitting
425
+ Scale-out your database layer without touching any model or query code. ArikaJS automatically routes `SELECT` queries to the read replica pool and `INSERT/UPDATE/DELETE` to the write master.
426
+ ```typescript
427
+ // config/database.ts
428
+ {
429
+ driver: 'mysql',
430
+ database: 'myapp',
431
+ // One write master
432
+ write: { host: 'db-master.internal', username: 'root', password: 'secret' },
433
+ // Multiple read replicas — one is chosen at random per query
434
+ read: [
435
+ { host: 'db-replica-1.internal' },
436
+ { host: 'db-replica-2.internal' },
437
+ ],
438
+ pool: { min: 2, max: 20 }
439
+ }
440
+ // No code changes needed — ArikaJS routes automatically!
441
+ ```
442
+
443
+ ### 💼 Transactions
444
+ Use `Database.transaction()` for clean, auto-rollback-on-error transactions. Nested calls automatically use SQL **savepoints**.
445
+ ```typescript
446
+ await Database.transaction(async (trx) => {
447
+ await DB.table('accounts').where('id', fromId).update({ balance: DB.raw('balance - 500') });
448
+ await DB.table('accounts').where('id', toId).update({ balance: DB.raw('balance + 500') });
449
+ await DB.table('transfers').insert({ from_id: fromId, to_id: toId, amount: 500 });
450
+ });
451
+ // If any statement throws, the entire block is rolled back automatically!
452
+
453
+ // Manual control:
454
+ const txm = await Database.getTransactionManager();
455
+ await txm.begin();
456
+ try {
457
+ await txm.commit();
458
+ } catch {
459
+ await txm.rollback();
460
+ }
461
+ ```
462
+
463
+ ### 👁️ Model Observers
464
+ Keep your models lean. Attach lifecycle listeners externally without polluting the model with inline logic.
465
+ ```typescript
466
+ class UserObserver {
467
+ async creating(user: User) {
468
+ user.role = user.role ?? 'member';
469
+ }
470
+
471
+ async created(user: User) {
472
+ await EmailService.sendWelcomeEmail(user.email);
473
+ }
474
+
475
+ // Return false to CANCEL the operation
476
+ async deleting(user: User) {
477
+ if (user.role === 'admin') return false; // Block admin deletions!
478
+ }
479
+
480
+ async deleted(user: User) {
481
+ await AuditLog.record(`User ${user.id} was deleted`);
482
+ }
483
+ }
484
+
485
+ // Register once in a ServiceProvider or boot file:
486
+ User.observe(new UserObserver());
487
+ ```
488
+
489
+ ### 🔍 Query Logging & Debug Mode
490
+ Introspect every SQL query fired — perfect for debugging slow endpoints or verifying your query plans.
491
+ ```typescript
492
+ // Enable logging:
493
+ Database.enableQueryLog();
494
+
495
+ await User.where('status', 'active').get();
496
+ await Post.where('user_id', 1).limit(5).get();
497
+
498
+ // Inspect queries:
499
+ const logs = Database.getQueryLog();
500
+ console.log(logs);
501
+ // [
502
+ // { sql: 'SELECT * FROM users WHERE status = ?', bindings: ['active'], time: 1.2, timestamp: Date },
503
+ // { sql: 'SELECT * FROM posts WHERE user_id = ? LIMIT ?', bindings: [1, 5], time: 0.8, timestamp: Date },
504
+ // ]
505
+
506
+ // Or listen live:
507
+ QueryLogger.listen((log) => {
508
+ if (log.time > 100) console.warn(`[SLOW QUERY] ${log.sql} (${log.time}ms)`);
509
+ });
510
+
511
+ Database.flushQueryLog(); // Clear when done
512
+ ```
513
+
514
+ ### 🌐 Global Scopes
515
+ Automatic query constraints applied to every query without the caller needing to remember them — ideal for multi-tenancy and similar patterns.
516
+ ```typescript
517
+ // Register a scope at boot time:
518
+ User.addGlobalScope('active_only', (builder) => {
519
+ builder.where('status', 'active');
520
+ });
521
+
522
+ // From now on ALL User queries are scoped:
523
+ await User.all(); // SELECT * FROM users WHERE status = 'active'
524
+ await User.where('role', 'admin').get(); // Adds status constraint automatically
525
+
526
+ // Escape the scope when needed:
527
+ await User.withoutGlobalScopes().all(); // SELECT * FROM users (no scope)
528
+
529
+ // Or use a full class:
530
+ class TenantScope implements GlobalScope {
531
+ constructor(private tenantId: number) {}
532
+ apply(builder: any) {
533
+ builder.where('tenant_id', this.tenantId);
534
+ }
535
+ }
536
+ Post.addGlobalScope('tenant', new TenantScope(currentTenantId));
537
+ ```
538
+
539
+ ### 🔗 Advanced Relationships
540
+
541
+ ArikaJS supports advanced relationship types, bringing you power usually found only in heavy, complex ORMs.
542
+
543
+ #### Through Relationships
544
+ Relate models through an intermediate model.
545
+ ```typescript
546
+ class Country extends Model {
547
+ // Country has many Posts, through Users
548
+ posts() {
549
+ return this.hasManyThrough(Post, User, 'country_id', 'user_id');
550
+ }
551
+ }
552
+ // Get all posts for users in a specific country
553
+ const posts = await country.posts().get();
554
+ ```
555
+
556
+ #### Polymorphic Relationships
557
+ A model can belong to more than one other model on a single association.
558
+ ```typescript
559
+ class Image extends Model {
560
+ imageable() {
561
+ return this.morphTo('imageable');
562
+ }
563
+ }
564
+
565
+ class Post extends Model {
566
+ image() {
567
+ return this.morphOne(Image, 'imageable');
568
+ }
569
+ }
570
+
571
+ class User extends Model {
572
+ image() {
573
+ return this.morphOne(Image, 'imageable');
574
+ }
575
+ }
576
+
577
+ // Inserting polymorphic models
578
+ const post = await Post.find(1);
579
+ await post.image().create({ url: '/foo.png' }); // sets imageable_id=1, imageable_type='Post'
580
+ ```
581
+
582
+ #### Pivot Table Operations (Many-to-Many)
583
+ ArikaJS provides robust helpers for `BelongsToMany` pivot tables:
584
+ ```typescript
585
+ // 1. Fetching extra pivot columns:
586
+ class User extends Model {
587
+ roles() {
588
+ return this.belongsToMany(Role).withPivot('expires_at', 'assigned_by');
589
+ }
590
+ }
591
+ const user = await User.find(1);
592
+ const firstRole = (await user.roles().get())[0];
593
+ console.log(firstRole.pivot.expires_at);
594
+
595
+ // 2. Modifying the relationship (Attach/Detach/Sync/Toggle)
596
+ const roleId = 5;
597
+
598
+ // Simply add the link:
599
+ await user.roles().attach(roleId, { assigned_by: 'admin' });
600
+
601
+ // Remove the link:
602
+ await user.roles().detach(roleId);
603
+
604
+ // Sync: Only keep these exact IDs and remove the rest
605
+ await user.roles().sync([1, 2, 3]);
606
+
607
+ // Toggle: Add if missing, remove if present
608
+ await user.roles().toggle([1, 4]);
609
+ ```
610
+
611
+ #### Relationship Counting
612
+ You can dynamically count the number of related objects without loading them simply by using `withCount()`. ArikaJS uses optimized subqueries.
613
+ ```typescript
614
+ const posts = await Post.withCount('comments').all();
615
+ console.log(posts[0].comments_count); // e.g: 4
616
+
617
+ // Count multiple relationships at once
618
+ const users = await User.withCount(['posts', 'followers']).all();
619
+ console.log(users[0].posts_count);
620
+ ```
621
+
622
+ ### 🔍 Enterprise ORM Features
623
+
624
+ #### Advanced Relationship Filtering
625
+ Filter parents based on the existence of children using `whereHas` or `whereDoesntHave`. This runs powerful `EXISTS` subqueries behind the scenes:
626
+ ```typescript
627
+ // Fetch users who have at least one published post
628
+ const activeBloggers = await User.whereHas('posts', q => q.where('status', 'published')).get();
629
+
630
+ // Fetch users with no comments
631
+ const lurkers = await User.whereDoesntHave('comments').get();
632
+ ```
633
+
634
+ #### API-Ready Pagination
635
+ Simply call `.paginate()`, `.simplePaginate()`, or `.cursorPaginate()` to instantly receive a JSON response structure that is ready to be sent to your frontend:
636
+ ```typescript
637
+ // Traditional offset pagination (page 2, 15 items per page)
638
+ const results = await User.paginate(2, 15, '/api/users');
639
+ /*
640
+ {
641
+ "data": [...],
642
+ "meta": { "total": 100, "current_page": 2, "last_page": 7, ... },
643
+ "links": { "prev_page_url": "/api/users?page=1", "next_page_url": "/api/users?page=3" }
644
+ }
645
+ */
646
+
647
+ // Ultra-fast Cursor pagination for infinite scrolling (WHERE id > last_id LIMIT)
648
+ const infinite = await User.cursorPaginate('cursor_id_here', 15);
649
+ ```
650
+
651
+ #### Memory-Safe Chunking
652
+ If you need to process tens of thousands of records, `all()` will crash your Node process. Use `chunk` to iterate over them safely:
653
+ ```typescript
654
+ // Fetches 1000 users at a time, keeping RAM usage perfectly flat
655
+ await User.chunk(1000, async (users, page) => {
656
+ for (const user of users) {
657
+ await emailService.sendNewsletter(user.email);
658
+ }
659
+ });
660
+ ```
661
+
662
+ #### Other Advanced Features include:
296
663
  - Subqueries
297
664
  - Union queries
298
665
  - Raw expressions
@@ -307,7 +674,7 @@ const users = await User.where('active', true)
307
674
  - One Query Builder
308
675
  - No hidden globals
309
676
  - Predictable SQL
310
- - Laravel-like DX, Node.js performance
677
+ - Elegant DX, Node.js performance
311
678
 
312
679
  ---
313
680
 
@@ -0,0 +1,81 @@
1
+ import { Connection, ConnectionConfig } from '../Contracts/Database';
2
+ import { MongoClient, Db, ClientSession } from 'mongodb';
3
+ /**
4
+ * MongoDB database connection
5
+ *
6
+ * Supports:
7
+ * - Single-host standalone
8
+ * - Replica sets with read/write splitting via ReadPreference
9
+ * - ACID transactions via Client Sessions (requires a replica set)
10
+ * - Connection pooling via the native MongoClient built-in pool
11
+ */
12
+ export declare class MongoDBConnection implements Connection {
13
+ private writeClient;
14
+ private readClient;
15
+ private writeDb;
16
+ private readDb;
17
+ private config;
18
+ private session;
19
+ private inTransaction;
20
+ constructor(config: ConnectionConfig);
21
+ /**
22
+ * Build a MongoDB connection URI from a config object
23
+ */
24
+ private buildUri;
25
+ /**
26
+ * Build a replica-set URI from an array of read-replica host configs
27
+ */
28
+ private buildReplicaSetUri;
29
+ /**
30
+ * Lazily establish both the write (primary) and read (secondary) connections
31
+ */
32
+ private ensureConnected;
33
+ /**
34
+ * Raw SQL is not supported in MongoDB.
35
+ * Use getDatabase() / getCollection() for native MongoDB operations.
36
+ */
37
+ query(_sql: string, _bindings?: any[]): Promise<any>;
38
+ /**
39
+ * Get the Db instance routed to the correct pool.
40
+ * - Read operations → secondary preferred (read replica)
41
+ * - Write operations → primary
42
+ * - During a transaction → always primary
43
+ */
44
+ getDatabase(forWrite?: boolean): Promise<Db>;
45
+ /**
46
+ * Convenience: get a collection automatically routed to the right pool
47
+ */
48
+ getCollection(name: string, forWrite?: boolean): Promise<import("mongodb").Collection<import("bson").Document>>;
49
+ /**
50
+ * Get the underlying write MongoClient instance
51
+ */
52
+ getClient(): Promise<MongoClient>;
53
+ /**
54
+ * Begin a MongoDB ACID transaction.
55
+ * Requires a replica set — will throw on a standalone server.
56
+ */
57
+ beginTransaction(): Promise<void>;
58
+ /**
59
+ * Commit the active transaction
60
+ */
61
+ commit(): Promise<void>;
62
+ /**
63
+ * Rollback (abort) the active transaction
64
+ */
65
+ rollback(): Promise<void>;
66
+ /**
67
+ * Get the active ClientSession — pass this to collection operations
68
+ * when running queries inside a transaction.
69
+ */
70
+ getSession(): ClientSession | null;
71
+ /**
72
+ * Close both read and write connections
73
+ */
74
+ close(): Promise<void>;
75
+ /**
76
+ * Returns the write Db instance as the generic driver handle
77
+ */
78
+ getDriver(): any;
79
+ getSchemaGrammar(): any;
80
+ }
81
+ //# sourceMappingURL=MongoDBConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MongoDBConnection.d.ts","sourceRoot":"","sources":["../../src/Connections/MongoDBConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAkB,aAAa,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,YAAW,UAAU;IAChD,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,aAAa,CAAkB;gBAE3B,MAAM,EAAE,gBAAgB;IAMpC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAShB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;YACW,eAAe;IA2C7B;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAO9D;;;;;OAKG;IACG,WAAW,CAAC,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,EAAE,CAAC;IAMzD;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe;IAK3D;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IAOvC;;;OAGG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAUvC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACH,UAAU,IAAI,aAAa,GAAG,IAAI;IAMlC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;IACH,SAAS,IAAI,GAAG;IAIhB,gBAAgB,IAAI,GAAG;CAM1B"}