@starbemtech/star-db-query-builder 1.1.0 → 1.2.1

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 (88) hide show
  1. package/.github/workflows/publish.yml +118 -0
  2. package/ARCHITECTURE.md +313 -0
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +1275 -210
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +131 -0
  9. package/coverage/lcov-report/base.css +224 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/favicon.png +0 -0
  12. package/coverage/lcov-report/index.html +131 -0
  13. package/coverage/lcov-report/mysqlClient.ts.html +685 -0
  14. package/coverage/lcov-report/pgClient.ts.html +823 -0
  15. package/coverage/lcov-report/prettify.css +1 -0
  16. package/coverage/lcov-report/prettify.js +2 -0
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +210 -0
  19. package/coverage/lcov.info +533 -0
  20. package/coverage/mysqlClient.ts.html +685 -0
  21. package/coverage/pgClient.ts.html +823 -0
  22. package/coverage/prettify.css +1 -0
  23. package/coverage/prettify.js +2 -0
  24. package/coverage/sort-arrow-sprite.png +0 -0
  25. package/coverage/sorter.js +210 -0
  26. package/dist/index.d.ts +5 -4
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/src/core/repository.d.ts +374 -0
  30. package/dist/src/core/repository.js +677 -0
  31. package/dist/src/core/repository.js.map +1 -0
  32. package/dist/src/{default → core}/types.d.ts +9 -1
  33. package/dist/src/{default → core}/types.js.map +1 -1
  34. package/dist/src/core/utils.d.ts +133 -0
  35. package/dist/src/{default → core}/utils.js +167 -0
  36. package/dist/src/core/utils.js.map +1 -0
  37. package/dist/src/db/IDatabaseClient.d.ts +10 -1
  38. package/dist/src/db/initDb.d.ts +120 -1
  39. package/dist/src/db/initDb.js +119 -0
  40. package/dist/src/db/initDb.js.map +1 -1
  41. package/dist/src/db/mysqlClient.d.ts +23 -1
  42. package/dist/src/db/mysqlClient.js +115 -0
  43. package/dist/src/db/mysqlClient.js.map +1 -1
  44. package/dist/src/db/pgClient.d.ts +22 -1
  45. package/dist/src/db/pgClient.js +127 -0
  46. package/dist/src/db/pgClient.js.map +1 -1
  47. package/dist/src/monitor/monitor.d.ts +6 -1
  48. package/dist/src/monitor/monitor.js +5 -0
  49. package/dist/src/monitor/monitor.js.map +1 -1
  50. package/dist/src/setupTests.d.ts +26 -0
  51. package/dist/src/setupTests.js +43 -0
  52. package/dist/src/setupTests.js.map +1 -0
  53. package/docs/INDEX.md +145 -0
  54. package/docs/methods/findFirst.md +394 -0
  55. package/docs/methods/findMany.md +587 -0
  56. package/docs/methods/insert.md +536 -0
  57. package/docs/methods/insertMany.md +627 -0
  58. package/docs/methods/joins.md +781 -0
  59. package/docs/methods/rawQuery.md +284 -0
  60. package/docs/methods/transactions.md +737 -0
  61. package/eslint.config.mjs +77 -0
  62. package/index.ts +5 -4
  63. package/jest.config.ts +23 -28
  64. package/package.json +52 -29
  65. package/scripts/release.sh +123 -0
  66. package/src/core/repository.ts +865 -0
  67. package/src/{default → core}/types.ts +11 -2
  68. package/src/{default → core}/utils.ts +168 -0
  69. package/src/db/IDatabaseClient.ts +11 -1
  70. package/src/db/__tests__/mysqlClient.test.ts +262 -0
  71. package/src/db/__tests__/pgClient.test.ts +260 -0
  72. package/src/db/initDb.ts +120 -1
  73. package/src/db/mysqlClient.ts +119 -3
  74. package/src/db/pgClient.ts +131 -3
  75. package/src/monitor/monitor.ts +5 -0
  76. package/src/setupTests.ts +45 -0
  77. package/tsconfig.test.json +21 -0
  78. package/.eslintignore +0 -4
  79. package/.eslintrc.json +0 -32
  80. package/dist/.eslintrc.json +0 -32
  81. package/dist/src/default/genericRepository.d.ts +0 -28
  82. package/dist/src/default/genericRepository.js +0 -284
  83. package/dist/src/default/genericRepository.js.map +0 -1
  84. package/dist/src/default/utils.d.ts +0 -9
  85. package/dist/src/default/utils.js.map +0 -1
  86. package/index.d.ts +0 -60
  87. package/src/default/genericRepository.ts +0 -461
  88. /package/dist/src/{default → core}/types.js +0 -0
package/README.md CHANGED
@@ -1,385 +1,1450 @@
1
- # NodeJS Star DB Query Builder
1
+ # Star DB Query Builder
2
+
3
+ A powerful and flexible database query builder library for Node.js applications, supporting PostgreSQL and MySQL databases with TypeScript support.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Features](#-features)
8
+ - [Installation](#-installation)
9
+ - [Quick Start](#-quick-start)
10
+ - [Database Initialization](#database-initialization)
11
+ - [Query Methods](#query-methods)
12
+ - [findFirst](#findfirst)
13
+ - [findMany](#findmany)
14
+ - [insert](#insert)
15
+ - [insertMany](#insertmany)
16
+ - [update](#update)
17
+ - [updateMany](#updatemany)
18
+ - [deleteOne](#deleteone)
19
+ - [deleteMany](#deletemany)
20
+ - [joins](#joins)
21
+ - [rawQuery](#rawquery)
22
+ - [Transactions](#transactions)
23
+ - [withTransaction](#withtransaction)
24
+ - [beginTransaction](#begintransaction)
25
+ - [Types and Interfaces](#types-and-interfaces)
26
+ - [Advanced Usage](#advanced-usage)
27
+ - [Monitoring](#monitoring)
28
+ - [Best Practices](#best-practices)
29
+ - [Error Handling](#error-handling)
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+
33
+ ## ✨ Features
34
+
35
+ ### 🔧 **Core Functionality**
36
+
37
+ - **🔄 Multi-Connection Support**: Connect simultaneously to multiple PostgreSQL and MySQL databases
38
+ - **🛡️ Type Safety**: Complete TypeScript support with strong typing
39
+ - **⚡ Auto Retry**: Automatic retry for transient errors (timeouts, lost connections)
40
+ - **📊 Monitoring**: Event system for monitoring and logging
41
+ - **🔍 Query Builder**: Fluent interface for building complex queries
42
+ - **📦 Batch Operations**: Optimized batch operations (insertMany, updateMany)
43
+
44
+ ### 🗄️ **Database Support**
45
+
46
+ - **PostgreSQL**: Complete support with extensions (unaccent)
47
+ - **MySQL**: Full compatibility with MySQL 5.7+
48
+ - **Connection Pooling**: Efficient connection management
49
+ - **Transaction Support**: Full ACID transaction support with automatic rollback
50
+ - **Raw SQL**: Execute custom SQL queries when needed
51
+
52
+ ### 🛠️ **Development Tools**
53
+
54
+ - **ESLint + Prettier**: Clean and consistent code
55
+ - **Jest**: Unit and integration tests
56
+ - **Husky**: Git hooks for code quality
57
+ - **TypeScript**: Compilation and typing
58
+
59
+ ## 📦 Installation
2
60
 
3
- 🎉 Welcome to the NodeJS Database Library! This library provides a set of robust methods to interact with your database seamlessly. With TypeScript support, it ensures type safety and great developer experience.
61
+ ```bash
62
+ npm install @starbemtech/star-db-query-builder
63
+ # or
64
+ pnpm add @starbemtech/star-db-query-builder
65
+ # or
66
+ yarn add @starbemtech/star-db-query-builder
67
+ ```
4
68
 
5
- ## Features
69
+ ## Quick Start
6
70
 
7
- - **Multi-Connection Support:** Simultaneously connect to multiple MySQL and PostgreSQL databases.
8
- - **Automatic Retry:** Automatically retries queries in case of transient errors (e.g. connection loss or timeouts).
9
- - **External Configuration:** Customize connection pool settings and retry parameters through external configuration.
10
- - **Monitoring and Logging:** Emits events during the connection and query lifecycle, making it easier to integrate with your logging and monitoring systems.
71
+ ```typescript
72
+ import {
73
+ initDb,
74
+ getDbClient,
75
+ findFirst,
76
+ insert,
77
+ } from '@starbemtech/star-db-query-builder'
78
+
79
+ // Initialize database connection
80
+ await initDb({
81
+ type: 'pg', // or 'mysql'
82
+ options: {
83
+ host: 'localhost',
84
+ port: 5432,
85
+ database: 'myapp',
86
+ user: 'username',
87
+ password: 'password',
88
+ },
89
+ })
11
90
 
12
- ## Installation
91
+ // Get database client
92
+ const dbClient = getDbClient()
13
93
 
14
- ```bash
15
- // Use npm
16
- $ npm install star-db-query-builder
17
-
18
- // Use yarn
19
- $ yarn add star-db-query-builder
94
+ // Find a user
95
+ const user = await findFirst({
96
+ tableName: 'users',
97
+ dbClient,
98
+ where: { email: { operator: '=', value: 'user@example.com' } },
99
+ })
20
100
 
21
- // Use pnpm
22
- $ pnpm install star-db-query-builder
101
+ // Insert a new user
102
+ const newUser = await insert({
103
+ tableName: 'users',
104
+ dbClient,
105
+ data: { name: 'John Doe', email: 'john@example.com' },
106
+ })
23
107
  ```
24
108
 
25
- ## Usage
109
+ ## Database Initialization
26
110
 
27
- ### Initialization
111
+ ### initDb
28
112
 
29
- First, initialize the database with the appropriate configuration.
113
+ Initializes a database connection with the specified configuration.
30
114
 
31
115
  ```typescript
32
- import { initDb, getDbClient, PoolConfig } from 'star-db-query-builder';
116
+ await initDb({
117
+ name?: string, // Optional client name (default: 'default')
118
+ type: 'pg' | 'mysql', // Database type
119
+ options: PoolConfig | MySqlPoolOptions, // Connection options
120
+ retryOptions?: RetryOptions, // Optional retry configuration
121
+ installUnaccentExtension?: boolean // PostgreSQL unaccent extension
122
+ })
123
+ ```
33
124
 
34
- // Use PostgresSQL
35
- const pgPoolOptions: PoolConfig = {
36
- host: process.env.PG_HOST,
37
- user: process.env.PG_USER,
38
- password: process.env.PG_PASS,
39
- database: process.env.PG_DB,
40
- // OR
41
- connectionURL: 'YOUR POSTGRES CONNECTION URL'
42
- max: Number(process.env.PG_POOL_MAX) || 10,
43
- connectionTimeoutMillis: Number(process.env.PG_CONN_TIMEOUT) || 0,
44
- // Other pool options as needed
45
- }
125
+ #### PostgreSQL Example
46
126
 
47
- initDb({
48
- name: 'pg-prod',
127
+ ```typescript
128
+ import { initDb } from '@starbemtech/star-db-query-builder'
129
+
130
+ await initDb({
131
+ name: 'main',
49
132
  type: 'pg',
50
- options: pgPoolOptions,
133
+ options: {
134
+ host: 'localhost',
135
+ port: 5432,
136
+ database: 'myapp',
137
+ user: 'postgres',
138
+ password: 'password',
139
+ max: 20,
140
+ idleTimeoutMillis: 30000,
141
+ connectionTimeoutMillis: 2000,
142
+ },
51
143
  retryOptions: {
52
144
  retries: 3,
53
145
  factor: 2,
54
146
  minTimeout: 1000,
55
- // Other retry parameters if needed
56
- }
57
- });
147
+ maxTimeout: 5000,
148
+ },
149
+ installUnaccentExtension: true,
150
+ })
151
+ ```
152
+
153
+ #### MySQL Example
58
154
 
59
- // User MySQL
60
- initDb({
61
- name: 'mysql-prod',
155
+ ```typescript
156
+ import { initDb } from '@starbemtech/star-db-query-builder'
157
+
158
+ await initDb({
159
+ name: 'analytics',
62
160
  type: 'mysql',
63
161
  options: {
64
- host: process.env.MYSQL_HOST,
65
- user: process.env.MYSQL_USER,
66
- password: process.env.MYSQL_PASS,
67
- database: process.env.MYSQL_DB,
68
- connectionLimit: Number(process.env.MYSQL_CONN_LIMIT) || 10,
69
- // OR
70
- url: 'YOUR MYSQL CONNECTION URL'
71
- // Other pool options as needed
162
+ host: 'localhost',
163
+ port: 3306,
164
+ database: 'analytics',
165
+ user: 'root',
166
+ password: 'password',
167
+ connectionLimit: 10,
168
+ acquireTimeout: 60000,
169
+ timeout: 60000,
72
170
  },
73
- retryOptions: {
74
- retries: 3,
75
- factor: 2,
76
- minTimeout: 1000,
77
- // Other retry parameters if needed
78
- }
79
- });
171
+ })
172
+ ```
173
+
174
+ ### getDbClient
175
+
176
+ Retrieves a database client by name.
177
+
178
+ ```typescript
179
+ const dbClient = getDbClient(name?: string)
180
+ ```
181
+
182
+ ```typescript
183
+ // Get default client
184
+ const defaultClient = getDbClient()
80
185
 
81
- // In your service, create an instance of getDbClient
82
- const dbClient = getDbClient('pg-prod');
186
+ // Get named client
187
+ const analyticsClient = getDbClient('analytics')
83
188
  ```
84
189
 
85
- ### Methods
190
+ ## Query Methods
86
191
 
87
- #### findFirst
192
+ ### findFirst
88
193
 
89
- Retrieve the first matching record from a table.
194
+ Finds the first record that matches the specified conditions.
90
195
 
91
196
  ```typescript
92
- import { findFirst } from 'star-db-query-builder'
197
+ const result = await findFirst<T>({
198
+ tableName: string,
199
+ dbClient: IDatabaseClient,
200
+ select?: string[],
201
+ where?: Conditions<T>,
202
+ groupBy?: string[],
203
+ orderBy?: OrderBy
204
+ })
205
+ ```
206
+
207
+ #### Examples
208
+
209
+ ```typescript
210
+ // Find user by email
211
+ const user = await findFirst({
212
+ tableName: 'users',
213
+ dbClient,
214
+ where: {
215
+ email: { operator: '=', value: 'user@example.com' },
216
+ },
217
+ })
93
218
 
94
- const result = await findFirst({
219
+ // Find with specific fields
220
+ const user = await findFirst({
95
221
  tableName: 'users',
96
222
  dbClient,
97
223
  select: ['id', 'name', 'email'],
98
224
  where: {
99
- id: { operator: '=', value: 1 },
225
+ status: { operator: '=', value: 'active' },
100
226
  },
101
227
  })
102
228
 
103
- console.log(result)
229
+ // Find with complex conditions
230
+ const user = await findFirst({
231
+ tableName: 'users',
232
+ dbClient,
233
+ where: {
234
+ AND: [
235
+ { email: { operator: '=', value: 'user@example.com' } },
236
+ { status: { operator: '=', value: 'active' } },
237
+ ],
238
+ },
239
+ })
240
+
241
+ // Find with ordering
242
+ const latestUser = await findFirst({
243
+ tableName: 'users',
244
+ dbClient,
245
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
246
+ })
104
247
  ```
105
248
 
106
- #### findMany
249
+ ### findMany
107
250
 
108
- Retrieve multiple records from a table.
251
+ Finds multiple records that match the specified conditions.
109
252
 
110
253
  ```typescript
111
- import { findMany } from 'star-db-query-builder'
254
+ const results = await findMany<T>({
255
+ tableName: string,
256
+ dbClient: IDatabaseClient,
257
+ select?: string[],
258
+ where?: Conditions<T>,
259
+ groupBy?: string[],
260
+ orderBy?: OrderBy,
261
+ limit?: number,
262
+ offset?: number,
263
+ unaccent?: boolean
264
+ })
265
+ ```
266
+
267
+ #### Examples
112
268
 
113
- const results = await findMany({
269
+ ```typescript
270
+ // Find all active users
271
+ const users = await findMany({
114
272
  tableName: 'users',
115
273
  dbClient,
116
- select: ['id', 'name', 'email'],
117
274
  where: {
118
- status: { operator: '= ', value: 'active' },
275
+ status: { operator: '=', value: 'active' },
119
276
  },
277
+ })
278
+
279
+ // Find with pagination
280
+ const users = await findMany({
281
+ tableName: 'users',
282
+ dbClient,
120
283
  limit: 10,
121
- offset: 0,
284
+ offset: 20,
285
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
286
+ })
287
+
288
+ // Find with complex conditions
289
+ const users = await findMany({
290
+ tableName: 'users',
291
+ dbClient,
292
+ where: {
293
+ OR: [
294
+ { status: { operator: '=', value: 'active' } },
295
+ { status: { operator: '=', value: 'pending' } },
296
+ ],
297
+ created_at: {
298
+ operator: '>=',
299
+ value: new Date('2023-01-01'),
300
+ },
301
+ },
122
302
  })
123
303
 
124
- console.log(results)
304
+ // Find with grouping
305
+ const userStats = await findMany({
306
+ tableName: 'users',
307
+ dbClient,
308
+ select: ['status', 'COUNT(*) as count'],
309
+ groupBy: ['status'],
310
+ })
125
311
  ```
126
312
 
127
- #### insert
313
+ ### insert
128
314
 
129
- Insert a new record into a table.
315
+ Inserts a single record into the database.
130
316
 
131
317
  ```typescript
132
- import { insert } from 'star-db-query-builder'
318
+ const result = await insert<P, R>({
319
+ tableName: string,
320
+ dbClient: IDatabaseClient,
321
+ data: P,
322
+ returning?: string[]
323
+ })
324
+ ```
133
325
 
134
- const newUser = { name: 'John Doe', email: 'john@example.com' }
326
+ #### Examples
135
327
 
136
- const insertedUser = await insert({
328
+ ```typescript
329
+ // Simple insert
330
+ const user = await insert({
137
331
  tableName: 'users',
138
332
  dbClient,
139
- data: newUser,
140
- returning: ['id', 'name', 'email'],
333
+ data: {
334
+ name: 'John Doe',
335
+ email: 'john@example.com',
336
+ age: 30,
337
+ },
338
+ })
339
+
340
+ // Insert with specific returning fields
341
+ const user = await insert({
342
+ tableName: 'users',
343
+ dbClient,
344
+ data: {
345
+ name: 'Jane Doe',
346
+ email: 'jane@example.com',
347
+ },
348
+ returning: ['id', 'name', 'email', 'created_at'],
141
349
  })
142
350
 
143
- console.log(insertedUser)
351
+ // Insert with TypeScript typing
352
+ interface UserData {
353
+ name: string
354
+ email: string
355
+ age: number
356
+ }
357
+
358
+ interface User {
359
+ id: string
360
+ name: string
361
+ email: string
362
+ age: number
363
+ created_at: Date
364
+ updated_at: Date
365
+ }
366
+
367
+ const user: User = await insert<UserData, User>({
368
+ tableName: 'users',
369
+ dbClient,
370
+ data: {
371
+ name: 'John Doe',
372
+ email: 'john@example.com',
373
+ age: 30,
374
+ },
375
+ })
144
376
  ```
145
377
 
146
- #### update
378
+ ### insertMany
147
379
 
148
- Update an existing record in a table.
380
+ Inserts multiple records into the database in a single operation.
149
381
 
150
382
  ```typescript
151
- import { update } from 'star-db-query-builder'
383
+ const results = await insertMany<P, R>({
384
+ tableName: string,
385
+ dbClient: IDatabaseClient,
386
+ data: P[],
387
+ returning?: string[]
388
+ })
389
+ ```
152
390
 
153
- const updatedUser = { name: 'John Smith' }
391
+ #### Examples
154
392
 
155
- const result = await update({
393
+ ```typescript
394
+ // Insert multiple users
395
+ const users = await insertMany({
396
+ tableName: 'users',
397
+ dbClient,
398
+ data: [
399
+ { name: 'John Doe', email: 'john@example.com' },
400
+ { name: 'Jane Doe', email: 'jane@example.com' },
401
+ { name: 'Bob Smith', email: 'bob@example.com' },
402
+ ],
403
+ })
404
+
405
+ // Insert with returning fields
406
+ const users = await insertMany({
156
407
  tableName: 'users',
157
408
  dbClient,
158
- id: 1,
159
- data: updatedUser,
409
+ data: [
410
+ { name: 'John Doe', email: 'john@example.com' },
411
+ { name: 'Jane Doe', email: 'jane@example.com' },
412
+ ],
160
413
  returning: ['id', 'name', 'email'],
161
414
  })
415
+ ```
416
+
417
+ ### update
418
+
419
+ Updates a single record by ID.
420
+
421
+ ```typescript
422
+ const result = await update<P, R>({
423
+ tableName: string,
424
+ dbClient: IDatabaseClient,
425
+ id: string,
426
+ data: P,
427
+ returning?: string[]
428
+ })
429
+ ```
162
430
 
163
- console.log(result)
431
+ #### Examples
432
+
433
+ ```typescript
434
+ // Simple update
435
+ const updatedUser = await update({
436
+ tableName: 'users',
437
+ dbClient,
438
+ id: 'user-123',
439
+ data: {
440
+ name: 'John Updated',
441
+ age: 31,
442
+ },
443
+ })
444
+
445
+ // Update with returning fields
446
+ const updatedUser = await update({
447
+ tableName: 'users',
448
+ dbClient,
449
+ id: 'user-123',
450
+ data: {
451
+ status: 'active',
452
+ last_login: new Date(),
453
+ },
454
+ returning: ['id', 'status', 'last_login', 'updated_at'],
455
+ })
164
456
  ```
165
457
 
166
- #### deleteOne
458
+ ### updateMany
167
459
 
168
- Delete a record from a table.
460
+ Updates multiple records based on specified conditions.
169
461
 
170
462
  ```typescript
171
- import { deleteOne } from 'star-db-query-builder'
463
+ const results = await updateMany<P, R>({
464
+ tableName: string,
465
+ dbClient: IDatabaseClient,
466
+ data: P,
467
+ where: Conditions<T>,
468
+ returning?: string[]
469
+ })
470
+ ```
471
+
472
+ #### Examples
473
+
474
+ ```typescript
475
+ // Update all inactive users
476
+ const updatedUsers = await updateMany({
477
+ tableName: 'users',
478
+ dbClient,
479
+ data: {
480
+ status: 'active',
481
+ updated_at: new Date(),
482
+ },
483
+ where: {
484
+ status: { operator: '=', value: 'inactive' },
485
+ },
486
+ })
487
+
488
+ // Update with complex conditions
489
+ const updatedUsers = await updateMany({
490
+ tableName: 'users',
491
+ dbClient,
492
+ data: {
493
+ last_login: new Date(),
494
+ login_count: { operator: '+', value: 1 },
495
+ },
496
+ where: {
497
+ AND: [
498
+ { status: { operator: '=', value: 'active' } },
499
+ { last_login: { operator: '<', value: new Date('2023-01-01') } },
500
+ ],
501
+ },
502
+ returning: ['id', 'name', 'last_login', 'login_count'],
503
+ })
504
+ ```
505
+
506
+ ### deleteOne
172
507
 
508
+ Deletes a single record by ID (soft delete by default).
509
+
510
+ ```typescript
511
+ await deleteOne<T>({
512
+ tableName: string,
513
+ dbClient: IDatabaseClient,
514
+ id: string,
515
+ permanently?: boolean
516
+ })
517
+ ```
518
+
519
+ #### Examples
520
+
521
+ ```typescript
522
+ // Soft delete (sets status to 'deleted')
173
523
  await deleteOne({
174
524
  tableName: 'users',
175
525
  dbClient,
176
- id: 1,
177
- permanently: true,
526
+ id: 'user-123',
178
527
  })
179
528
 
180
- console.log('User deleted')
529
+ // Permanent delete
530
+ await deleteOne({
531
+ tableName: 'users',
532
+ dbClient,
533
+ id: 'user-123',
534
+ permanently: true,
535
+ })
181
536
  ```
182
537
 
183
- #### joins
538
+ ### deleteMany
539
+
540
+ Deletes multiple records by IDs (soft delete by default).
541
+
542
+ ```typescript
543
+ await deleteMany<T>({
544
+ tableName: string,
545
+ dbClient: IDatabaseClient,
546
+ ids: string[] | number[],
547
+ field?: string,
548
+ permanently?: boolean
549
+ })
550
+ ```
184
551
 
185
- Execute a join query.
552
+ #### Examples
186
553
 
187
554
  ```typescript
188
- import { joins } from 'star-db-query-builder'
555
+ // Soft delete multiple users
556
+ await deleteMany({
557
+ tableName: 'users',
558
+ dbClient,
559
+ ids: ['user-1', 'user-2', 'user-3'],
560
+ })
189
561
 
190
- const joinResults = await joins({
562
+ // Permanent delete with custom field
563
+ await deleteMany({
191
564
  tableName: 'orders',
192
565
  dbClient,
193
- select: ['orders.id', 'users.name'],
566
+ ids: [1, 2, 3],
567
+ field: 'order_id',
568
+ permanently: true,
569
+ })
570
+ ```
571
+
572
+ ### joins
573
+
574
+ Executes queries with JOIN operations.
575
+
576
+ ```typescript
577
+ const results = await joins<T>({
578
+ tableName: string,
579
+ dbClient: IDatabaseClient,
580
+ select: string[],
581
+ joins: JoinClause[],
582
+ where?: Conditions<T>,
583
+ groupBy?: string[],
584
+ orderBy?: OrderBy,
585
+ limit?: number,
586
+ offset?: number,
587
+ unaccent?: boolean
588
+ })
589
+ ```
590
+
591
+ #### Examples
592
+
593
+ ```typescript
594
+ // Simple JOIN
595
+ const usersWithOrders = await joins({
596
+ tableName: 'users',
597
+ dbClient,
598
+ select: ['users.id', 'users.name', 'orders.total'],
194
599
  joins: [
195
600
  {
196
- table: 'users',
197
- on: { 'orders.userId': 'users.id' },
601
+ type: 'LEFT',
602
+ table: 'orders',
603
+ on: 'users.id = orders.user_id',
198
604
  },
199
605
  ],
200
606
  where: {
201
- JOINS: [
202
- {
203
- 'users.id': { operator: '=', value: exist.user_id },
204
- },
205
- ],
607
+ 'users.status': { operator: '=', value: 'active' },
206
608
  },
207
609
  })
208
610
 
209
- console.log(joinResults)
611
+ // Multiple JOINs
612
+ const report = await joins({
613
+ tableName: 'users',
614
+ dbClient,
615
+ select: [
616
+ 'users.name',
617
+ 'users.email',
618
+ 'COUNT(orders.id) as order_count',
619
+ 'SUM(orders.total) as total_spent',
620
+ 'plans.name as plan_name',
621
+ ],
622
+ joins: [
623
+ {
624
+ type: 'LEFT',
625
+ table: 'orders',
626
+ on: 'users.id = orders.user_id',
627
+ },
628
+ {
629
+ type: 'LEFT',
630
+ table: 'user_plans',
631
+ on: 'users.id = user_plans.user_id',
632
+ },
633
+ {
634
+ type: 'LEFT',
635
+ table: 'plans',
636
+ on: 'user_plans.plan_id = plans.id',
637
+ },
638
+ ],
639
+ groupBy: ['users.id', 'users.name', 'users.email', 'plans.name'],
640
+ having: {
641
+ 'COUNT(orders.id)': { operator: '>', value: 0 },
642
+ },
643
+ orderBy: [{ field: 'total_spent', direction: 'DESC' }],
644
+ })
210
645
  ```
211
646
 
212
- #### insertMany
647
+ ### rawQuery
213
648
 
214
- Insert multiple records into a table at once, optimizing performance for batch operations.
649
+ Executes raw SQL queries directly on the database.
215
650
 
216
- #### Parameters
217
-
218
- - `tableName`: Name of the table
219
- - `dbClient`: Database client (PostgreSQL or MySQL)
220
- - `data`: Array of objects with the data to be inserted
221
- - `returning` (optional): Array of fields to be returned after insertion
651
+ ```typescript
652
+ const result = await rawQuery<T>({
653
+ dbClient: IDatabaseClient,
654
+ sql: string,
655
+ params?: any[]
656
+ })
657
+ ```
222
658
 
223
- #### Usage Example
659
+ #### Examples
224
660
 
225
661
  ```typescript
226
- import { insertMany } from 'star-db-query-builder'
662
+ // Simple raw query
663
+ const users = await rawQuery({
664
+ dbClient,
665
+ sql: 'SELECT * FROM users WHERE active = true',
666
+ })
227
667
 
228
- // Data for insertion
229
- const usersData = [
230
- { name: 'João Silva', email: 'joao@example.com', age: 30 },
231
- { name: 'Maria Santos', email: 'maria@example.com', age: 25 },
232
- { name: 'Pedro Costa', email: 'pedro@example.com', age: 35 },
233
- ]
668
+ // Raw query with parameters
669
+ const user = await rawQuery({
670
+ dbClient,
671
+ sql: 'SELECT * FROM users WHERE id = ? AND email = ?',
672
+ params: ['user-123', 'user@example.com'],
673
+ })
234
674
 
235
- // Insert multiple users
236
- const insertedUsers = await insertMany({
237
- tableName: 'users',
238
- dbClient: dbClient,
239
- data: usersData,
240
- returning: ['id', 'name', 'email', 'created_at'],
675
+ // Complex aggregation
676
+ const stats = await rawQuery({
677
+ dbClient,
678
+ sql: `
679
+ SELECT
680
+ COUNT(*) as total_users,
681
+ AVG(age) as avg_age,
682
+ MAX(created_at) as last_created
683
+ FROM users
684
+ WHERE created_at >= ?
685
+ `,
686
+ params: [new Date('2023-01-01')],
241
687
  })
688
+ ```
689
+
690
+ ## Transactions
691
+
692
+ Execute multiple database operations within a single transaction to ensure data consistency and atomicity.
693
+
694
+ ### withTransaction
242
695
 
243
- console.log('Users inserted:', insertedUsers)
696
+ Executes a function within a database transaction with automatic commit/rollback handling.
697
+
698
+ ```typescript
699
+ const result = await withTransaction<T>(
700
+ dbClient: IDatabaseClient,
701
+ transactionFn: (tx: ITransactionClient) => Promise<T>
702
+ ): Promise<T>
244
703
  ```
245
704
 
246
- #### Features
705
+ #### Examples
247
706
 
248
- - **Automatic UUID Generation**: Each record receives a unique ID automatically
249
- - **Automatic Timestamp**: The `updated_at` field is filled automatically
250
- - **Support for PostgreSQL and MySQL**: Works with both databases
251
- - **Data Return**: Can return specific fields after insertion
252
- - **Optimized Performance**: Uses a single query to insert all records
707
+ ```typescript
708
+ import {
709
+ withTransaction,
710
+ insert,
711
+ update,
712
+ } from '@starbemtech/star-db-query-builder'
713
+
714
+ // Create user with profile in a single transaction
715
+ const createUserWithProfile = async (userData: any, profileData: any) => {
716
+ return withTransaction(dbClient, async (tx) => {
717
+ // Create user
718
+ const user = await insert({
719
+ tableName: 'users',
720
+ dbClient: tx,
721
+ data: userData,
722
+ })
723
+
724
+ // Create user profile
725
+ const profile = await insert({
726
+ tableName: 'user_profiles',
727
+ dbClient: tx,
728
+ data: {
729
+ ...profileData,
730
+ user_id: user.id,
731
+ },
732
+ })
253
733
 
254
- #### Database Behavior
734
+ return { user, profile }
735
+ })
736
+ }
255
737
 
256
- **PostgreSQL**: Uses the `RETURNING` clause to return inserted data
257
- **MySQL**: Executes a separate query to fetch inserted records
738
+ // E-commerce order processing
739
+ const processOrder = async (orderData: any, orderItems: any[]) => {
740
+ return withTransaction(dbClient, async (tx) => {
741
+ // Create order
742
+ const order = await insert({
743
+ tableName: 'orders',
744
+ dbClient: tx,
745
+ data: {
746
+ ...orderData,
747
+ status: 'pending',
748
+ total: 0,
749
+ },
750
+ })
751
+
752
+ let totalAmount = 0
753
+
754
+ // Create order items and calculate total
755
+ for (const item of orderItems) {
756
+ await insert({
757
+ tableName: 'order_items',
758
+ dbClient: tx,
759
+ data: {
760
+ ...item,
761
+ order_id: order.id,
762
+ },
763
+ })
764
+
765
+ totalAmount += item.price * item.quantity
766
+
767
+ // Update product stock
768
+ await update({
769
+ tableName: 'products',
770
+ dbClient: tx,
771
+ id: item.product_id,
772
+ data: {
773
+ stock: { operator: '-', value: item.quantity },
774
+ },
775
+ })
776
+ }
777
+
778
+ // Update order total
779
+ await update({
780
+ tableName: 'orders',
781
+ dbClient: tx,
782
+ id: order.id,
783
+ data: {
784
+ total: totalAmount,
785
+ status: 'confirmed',
786
+ },
787
+ })
258
788
 
259
- #### Validations
789
+ return { order, totalAmount }
790
+ })
791
+ }
792
+ ```
260
793
 
261
- - Checks if the table name was provided
262
- - Checks if the database client was provided
263
- - Checks if the data array is not empty
264
- - Ensures that all items have the same structure of fields
794
+ ### beginTransaction
265
795
 
266
- #### updateMany
796
+ Creates a transaction client for manual transaction management.
267
797
 
268
- Updates multiple records in a table based on a where condition, optimizing performance for batch operations.
798
+ ```typescript
799
+ const transaction = await beginTransaction(dbClient: IDatabaseClient): Promise<ITransactionClient>
800
+ ```
269
801
 
270
- #### Parameters
802
+ #### Examples
271
803
 
272
- - `tableName`: Name of the table
273
- - `dbClient`: Database client (PostgreSQL or MySQL)
274
- - `data`: Object with the data to be updated
275
- - `where`: Where condition to filter which records to update
276
- - `returning` (optional): Array of fields to be returned after update
804
+ ```typescript
805
+ import {
806
+ beginTransaction,
807
+ insert,
808
+ update,
809
+ } from '@starbemtech/star-db-query-builder'
810
+
811
+ // Manual transaction management
812
+ const complexOperation = async () => {
813
+ const transaction = await beginTransaction(dbClient)
814
+
815
+ try {
816
+ // First operation
817
+ const user = await insert({
818
+ tableName: 'users',
819
+ dbClient: transaction,
820
+ data: { name: 'John Doe', email: 'john@example.com' },
821
+ })
822
+
823
+ // Second operation
824
+ const profile = await insert({
825
+ tableName: 'user_profiles',
826
+ dbClient: transaction,
827
+ data: { user_id: user.id, bio: 'Hello world' },
828
+ })
829
+
830
+ // Third operation
831
+ await update({
832
+ tableName: 'users',
833
+ dbClient: transaction,
834
+ id: user.id,
835
+ data: { profile_created: true },
836
+ })
837
+
838
+ // Commit all changes
839
+ await transaction.commit()
840
+ return { user, profile }
841
+ } catch (error) {
842
+ // Rollback on any error
843
+ await transaction.rollback()
844
+ throw error
845
+ }
846
+ }
847
+ ```
277
848
 
278
- #### Usage Example
849
+ ### ITransactionClient Interface
279
850
 
280
851
  ```typescript
281
- import { updateMany } from 'star-db-query-builder'
852
+ interface ITransactionClient {
853
+ query: <T>(sql: string, params?: any[]) => Promise<T>
854
+ commit: () => Promise<void>
855
+ rollback: () => Promise<void>
856
+ }
857
+ ```
858
+
859
+ ## Types and Interfaces
860
+
861
+ ### Conditions
862
+
863
+ Used for building WHERE clauses with type safety.
282
864
 
283
- // Update data
284
- const updateData = {
285
- status: 'active',
286
- updated_at: new Date(),
865
+ ```typescript
866
+ type Conditions<T> = {
867
+ [P in keyof T]?: Condition<T[P]>
868
+ } & LogicalCondition<T>
869
+
870
+ type Condition<T> = OperatorCondition | LogicalCondition<T>
871
+
872
+ interface OperatorCondition {
873
+ operator:
874
+ | '='
875
+ | '!='
876
+ | '>'
877
+ | '<'
878
+ | '>='
879
+ | '<='
880
+ | 'LIKE'
881
+ | 'NOT LIKE'
882
+ | 'ILIKE'
883
+ | 'IN'
884
+ | 'NOT IN'
885
+ | 'BETWEEN'
886
+ | 'IS NULL'
887
+ | 'IS NOT NULL'
888
+ | 'NOT EXISTS'
889
+ value: SimpleValue | SimpleValue[]
287
890
  }
288
891
 
289
- // Where condition
290
- const whereCondition = {
291
- status: { operator: '=', value: 'pending' },
292
- created_at: { operator: '<', value: new Date('2024-01-01') },
892
+ interface LogicalCondition<T> {
893
+ OR?: Conditions<T>[]
894
+ AND?: Conditions<T>[]
895
+ JOINS?: Conditions<object>
896
+ notExists?: OperatorCondition
293
897
  }
898
+ ```
294
899
 
295
- // Update multiple users
296
- const updatedUsers = await updateMany({
297
- tableName: 'users',
298
- dbClient: dbClient,
299
- data: updateData,
300
- where: whereCondition,
301
- returning: ['id', 'name', 'email', 'status', 'updated_at'],
302
- })
900
+ ### OrderBy
901
+
902
+ Used for specifying sort order.
303
903
 
304
- console.log('Users updated:', updatedUsers)
904
+ ```typescript
905
+ type OrderBy = { field: string; direction: 'ASC' | 'DESC' }[]
906
+ ```
907
+
908
+ ### JoinClause
909
+
910
+ Used for JOIN operations.
911
+
912
+ ```typescript
913
+ interface JoinClause {
914
+ type: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL'
915
+ table: string
916
+ on: string
917
+ }
305
918
  ```
306
919
 
307
- #### Features
920
+ ## Advanced Usage
308
921
 
309
- - **Batch Update**: Updates multiple records with a single query
310
- - **Conditional Update**: Uses where conditions to filter which records to update
311
- - **Support for PostgreSQL and MySQL**: Works with both databases
312
- - **Data Return**: Can return specific fields after the update
313
- - **Optimized Performance**: Uses a single query to update all matching records
922
+ ### Complex WHERE Conditions
314
923
 
315
- #### Database Behavior
924
+ ```typescript
925
+ const users = await findMany({
926
+ tableName: 'users',
927
+ dbClient,
928
+ where: {
929
+ AND: [
930
+ { status: { operator: '=', value: 'active' } },
931
+ {
932
+ OR: [
933
+ { age: { operator: '>=', value: 18 } },
934
+ { verified: { operator: '=', value: true } },
935
+ ],
936
+ },
937
+ { created_at: { operator: '>=', value: new Date('2023-01-01') } },
938
+ ],
939
+ },
940
+ })
941
+ ```
316
942
 
317
- **PostgreSQL**: Uses the `RETURNING` clause to return updated data
318
- **MySQL**: Executes a separate query to fetch updated records
943
+ ### Using Unaccent for PostgreSQL
319
944
 
320
- #### Validations
945
+ ```typescript
946
+ const users = await findMany({
947
+ tableName: 'users',
948
+ dbClient,
949
+ where: {
950
+ name: { operator: 'ILIKE', value: '%joão%' },
951
+ },
952
+ unaccent: true, // Enables unaccent search
953
+ })
954
+ ```
321
955
 
322
- - Checks if the table name was provided
323
- - Checks if the database client was provided
324
- - Checks if the data object was provided
325
- - Checks if the where condition was provided
956
+ ### Using Unaccent for PostgreSQL
326
957
 
327
- ### Monitoring and Logging
958
+ ```typescript
959
+ const users = await findMany({
960
+ tableName: 'users',
961
+ dbClient,
962
+ where: {
963
+ name: { operator: 'ILIKE', value: '%joão%' },
964
+ },
965
+ unaccent: true, // Enables unaccent search
966
+ })
967
+ ```
328
968
 
329
- The library provides a monitoring module that emits key events during the lifecycle of connections and queries. You can subscribe to these events to integrate with your logging or monitoring system.
969
+ ## Monitoring
330
970
 
331
- #### Available Events
971
+ The library provides a comprehensive monitoring system to track database operations and performance.
332
972
 
333
- CONNECTION_CREATED: Emitted when a new connection (pool) is established.
334
- QUERY_START: Emitted just before a query starts executing.
335
- QUERY_END: Emitted after a query completes, including its execution time.
336
- QUERY_ERROR: Emitted when an error occurs during query execution.
337
- RETRY_ATTEMPT: Emitted when a query is retried due to a transient error.
973
+ ### Monitor Events
338
974
 
339
- ```ts
975
+ ```typescript
340
976
  import { monitor, MonitorEvents } from '@starbemtech/star-db-query-builder'
341
977
 
978
+ // Monitor connection events
342
979
  monitor.on(MonitorEvents.CONNECTION_CREATED, (data) => {
343
- console.log('Connection created:', data)
980
+ console.log('Database connection created:', data)
344
981
  })
345
982
 
983
+ // Monitor query events
346
984
  monitor.on(MonitorEvents.QUERY_START, (data) => {
347
- console.log('Query started:', data)
985
+ console.log('Query started:', {
986
+ sql: data.sql,
987
+ params: data.params,
988
+ clientType: data.clientType,
989
+ attempt: data.attempt,
990
+ })
348
991
  })
349
992
 
350
993
  monitor.on(MonitorEvents.QUERY_END, (data) => {
351
- console.log('Query finished:', data)
994
+ console.log('Query completed:', {
995
+ elapsedTime: data.elapsedTime,
996
+ clientType: data.clientType,
997
+ })
352
998
  })
353
999
 
354
1000
  monitor.on(MonitorEvents.QUERY_ERROR, (data) => {
355
- console.error('Query error:', data)
1001
+ console.error('Query failed:', {
1002
+ error: data.error,
1003
+ sql: data.sql,
1004
+ elapsedTime: data.elapsedTime,
1005
+ })
356
1006
  })
357
1007
 
1008
+ // Monitor transaction events
1009
+ monitor.on(MonitorEvents.TRANSACTION_COMMIT, (data) => {
1010
+ console.log('Transaction committed:', data)
1011
+ })
1012
+
1013
+ monitor.on(MonitorEvents.TRANSACTION_ROLLBACK, (data) => {
1014
+ console.log('Transaction rolled back:', data)
1015
+ })
1016
+
1017
+ // Monitor retry attempts
358
1018
  monitor.on(MonitorEvents.RETRY_ATTEMPT, (data) => {
359
- console.warn('Retrying query:', data)
1019
+ console.warn('Retry attempt:', {
1020
+ attempt: data.attempt,
1021
+ error: data.error,
1022
+ sql: data.sql,
1023
+ })
1024
+ })
1025
+ ```
1026
+
1027
+ ### Custom Monitoring Implementation
1028
+
1029
+ ```typescript
1030
+ // Example: Log all database operations to a file
1031
+ import fs from 'fs'
1032
+ import path from 'path'
1033
+
1034
+ const logFile = path.join(__dirname, 'database.log')
1035
+
1036
+ monitor.on(MonitorEvents.QUERY_START, (data) => {
1037
+ const logEntry = {
1038
+ timestamp: new Date().toISOString(),
1039
+ event: 'QUERY_START',
1040
+ sql: data.sql,
1041
+ params: data.params,
1042
+ clientType: data.clientType,
1043
+ }
1044
+
1045
+ fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n')
1046
+ })
1047
+
1048
+ monitor.on(MonitorEvents.QUERY_END, (data) => {
1049
+ const logEntry = {
1050
+ timestamp: new Date().toISOString(),
1051
+ event: 'QUERY_END',
1052
+ elapsedTime: data.elapsedTime,
1053
+ clientType: data.clientType,
1054
+ }
1055
+
1056
+ fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n')
360
1057
  })
361
1058
  ```
362
1059
 
363
- ## Customizing the Retry Strategy
1060
+ ### Performance Monitoring
1061
+
1062
+ ```typescript
1063
+ // Track slow queries
1064
+ monitor.on(MonitorEvents.QUERY_END, (data) => {
1065
+ if (data.elapsedTime > 1000) {
1066
+ // Queries taking more than 1 second
1067
+ console.warn('Slow query detected:', {
1068
+ sql: data.sql,
1069
+ elapsedTime: data.elapsedTime,
1070
+ clientType: data.clientType,
1071
+ })
1072
+ }
1073
+ })
364
1074
 
365
- The automatic retry mechanism leverages the promise-retry library. You can customize the following parameters:
1075
+ // Track connection pool usage
1076
+ monitor.on(MonitorEvents.CONNECTION_CREATED, (data) => {
1077
+ console.log('Connection pool status:', {
1078
+ clientType: data.clientType,
1079
+ poolOptions: data.poolOptions,
1080
+ })
1081
+ })
1082
+ ```
366
1083
 
367
- - retries: Number of retry attempts.
368
- - factor: Exponential backoff factor.
369
- - minTimeout: Minimum time (in milliseconds) to wait between retry attempts.
1084
+ ## Best Practices
370
1085
 
371
- These parameters are passed via the retryOptions property when initializing the connection.
1086
+ ### 1. Use TypeScript Types
1087
+
1088
+ ```typescript
1089
+ interface User {
1090
+ id: string
1091
+ name: string
1092
+ email: string
1093
+ created_at: Date
1094
+ }
1095
+
1096
+ const users: User[] = await findMany<User>({
1097
+ tableName: 'users',
1098
+ dbClient,
1099
+ where: { status: { operator: '=', value: 'active' } },
1100
+ })
1101
+ ```
1102
+
1103
+ ### 2. Use Specific Field Selection
1104
+
1105
+ ```typescript
1106
+ // Good: Select only needed fields
1107
+ const users = await findMany({
1108
+ tableName: 'users',
1109
+ dbClient,
1110
+ select: ['id', 'name', 'email'],
1111
+ where: { status: { operator: '=', value: 'active' } },
1112
+ })
1113
+
1114
+ // Avoid: Selecting all fields when not needed
1115
+ const users = await findMany({
1116
+ tableName: 'users',
1117
+ dbClient,
1118
+ where: { status: { operator: '=', value: 'active' } },
1119
+ })
1120
+ ```
1121
+
1122
+ ### 3. Use Pagination for Large Datasets
1123
+
1124
+ ```typescript
1125
+ const users = await findMany({
1126
+ tableName: 'users',
1127
+ dbClient,
1128
+ limit: 50,
1129
+ offset: 0,
1130
+ orderBy: [{ field: 'created_at', direction: 'DESC' }],
1131
+ })
1132
+ ```
1133
+
1134
+ ### 4. Use Batch Operations When Possible
1135
+
1136
+ ```typescript
1137
+ // Good: Batch insert
1138
+ const users = await insertMany({
1139
+ tableName: 'users',
1140
+ dbClient,
1141
+ data: userArray,
1142
+ })
1143
+
1144
+ // Avoid: Multiple individual inserts
1145
+ for (const user of userArray) {
1146
+ await insert({ tableName: 'users', dbClient, data: user })
1147
+ }
1148
+ ```
1149
+
1150
+ ### 5. Handle Errors Properly
1151
+
1152
+ ```typescript
1153
+ try {
1154
+ const user = await findFirst({
1155
+ tableName: 'users',
1156
+ dbClient,
1157
+ where: { email: { operator: '=', value: 'user@example.com' } },
1158
+ })
1159
+ } catch (error) {
1160
+ console.error('Database error:', error.message)
1161
+ // Handle error appropriately
1162
+ }
1163
+ ```
1164
+
1165
+ ### 6. Use Raw Queries Sparingly
1166
+
1167
+ ```typescript
1168
+ // Use built-in methods when possible
1169
+ const users = await findMany({
1170
+ tableName: 'users',
1171
+ dbClient,
1172
+ where: { status: { operator: '=', value: 'active' } },
1173
+ })
1174
+
1175
+ // Use rawQuery only for complex operations
1176
+ const complexStats = await rawQuery({
1177
+ dbClient,
1178
+ sql: 'SELECT ... complex aggregation ...',
1179
+ })
1180
+ ```
1181
+
1182
+ ### 7. Use Transactions for Data Consistency
1183
+
1184
+ ```typescript
1185
+ // Good: Use transactions for related operations
1186
+ const createUserWithProfile = async (userData: any, profileData: any) => {
1187
+ return withTransaction(dbClient, async (tx) => {
1188
+ const user = await insert({
1189
+ tableName: 'users',
1190
+ dbClient: tx,
1191
+ data: userData,
1192
+ })
1193
+
1194
+ await insert({
1195
+ tableName: 'user_profiles',
1196
+ dbClient: tx,
1197
+ data: { ...profileData, user_id: user.id },
1198
+ })
1199
+
1200
+ return user
1201
+ })
1202
+ }
1203
+
1204
+ // Avoid: Multiple separate operations without transactions
1205
+ const badUserCreation = async (userData: any, profileData: any) => {
1206
+ const user = await insert({
1207
+ tableName: 'users',
1208
+ dbClient,
1209
+ data: userData,
1210
+ })
1211
+
1212
+ // If this fails, the user will be created but profile won't
1213
+ await insert({
1214
+ tableName: 'user_profiles',
1215
+ dbClient,
1216
+ data: { ...profileData, user_id: user.id },
1217
+ })
1218
+
1219
+ return user
1220
+ }
1221
+ ```
1222
+
1223
+ ### 8. Keep Transactions Short
1224
+
1225
+ ```typescript
1226
+ // Good: Short, focused transaction
1227
+ const updateUserStatus = async (userId: string, status: string) => {
1228
+ return withTransaction(dbClient, async (tx) => {
1229
+ await update({
1230
+ tableName: 'users',
1231
+ dbClient: tx,
1232
+ id: userId,
1233
+ data: { status },
1234
+ })
1235
+
1236
+ await insert({
1237
+ tableName: 'user_status_history',
1238
+ dbClient: tx,
1239
+ data: { user_id: userId, status, changed_at: new Date() },
1240
+ })
1241
+ })
1242
+ }
1243
+
1244
+ // Avoid: Long-running transactions
1245
+ const badTransaction = async () => {
1246
+ return withTransaction(dbClient, async (tx) => {
1247
+ // ... many operations
1248
+ await someSlowOperation() // This could timeout
1249
+ // ... more operations
1250
+ })
1251
+ }
1252
+ ```
1253
+
1254
+ ## Error Handling
1255
+
1256
+ The library throws descriptive errors for common issues:
1257
+
1258
+ ### Common Errors
1259
+
1260
+ - `Table name is required`
1261
+ - `DB client is required`
1262
+ - `Data object is required`
1263
+ - `ID is required`
1264
+ - `Where condition is required`
1265
+ - `Raw query execution failed: [database message]`
1266
+ - `Transaction execution failed: [database message]`
1267
+
1268
+ ### Transaction Error Handling
1269
+
1270
+ ```typescript
1271
+ import {
1272
+ withTransaction,
1273
+ insert,
1274
+ update,
1275
+ } from '@starbemtech/star-db-query-builder'
1276
+
1277
+ const safeTransaction = async () => {
1278
+ try {
1279
+ return await withTransaction(dbClient, async (tx) => {
1280
+ // Transaction operations
1281
+ const result = await someOperation(tx)
1282
+ return result
1283
+ })
1284
+ } catch (error) {
1285
+ // Transaction was automatically rolled back
1286
+ console.error('Transaction failed:', error.message)
1287
+
1288
+ // Handle specific error types
1289
+ if (error.message.includes('deadlock detected')) {
1290
+ // Handle deadlock - you might want to retry
1291
+ console.warn('Deadlock detected, retrying...')
1292
+ // Implement retry logic
1293
+ } else if (error.message.includes('serialization failure')) {
1294
+ // Handle serialization failure
1295
+ console.warn('Serialization failure, retrying...')
1296
+ // Implement retry logic
1297
+ } else if (error.message.includes('connection lost')) {
1298
+ // Handle connection issues
1299
+ console.error('Database connection lost')
1300
+ // Implement reconnection logic
1301
+ } else {
1302
+ // Handle other errors
1303
+ console.error('Transaction error:', error.message)
1304
+ }
1305
+
1306
+ throw error
1307
+ }
1308
+ }
1309
+ ```
1310
+
1311
+ ### Retry Logic for Transient Errors
1312
+
1313
+ ```typescript
1314
+ const retryTransaction = async <T>(
1315
+ transactionFn: (tx: ITransactionClient) => Promise<T>,
1316
+ maxRetries: number = 3
1317
+ ): Promise<T> => {
1318
+ let lastError: Error
1319
+
1320
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
1321
+ try {
1322
+ return await withTransaction(dbClient, transactionFn)
1323
+ } catch (error) {
1324
+ lastError = error as Error
1325
+
1326
+ // Check if error is retryable
1327
+ if (isRetryableError(error) && attempt < maxRetries) {
1328
+ const delay = Math.pow(2, attempt) * 1000 // Exponential backoff
1329
+ console.warn(
1330
+ `Transaction attempt ${attempt} failed, retrying in ${delay}ms...`
1331
+ )
1332
+ await new Promise((resolve) => setTimeout(resolve, delay))
1333
+ continue
1334
+ }
1335
+
1336
+ throw error
1337
+ }
1338
+ }
1339
+
1340
+ throw lastError!
1341
+ }
1342
+
1343
+ const isRetryableError = (error: any): boolean => {
1344
+ const retryableErrors = [
1345
+ 'deadlock detected',
1346
+ 'serialization failure',
1347
+ 'connection lost',
1348
+ 'timeout',
1349
+ ]
1350
+
1351
+ return retryableErrors.some((msg) =>
1352
+ error.message?.toLowerCase().includes(msg)
1353
+ )
1354
+ }
1355
+ ```
1356
+
1357
+ Always wrap database operations in try-catch blocks and handle errors appropriately in your application.
372
1358
 
373
1359
  ## Contributing
374
1360
 
375
- Feel free to contribute by opening pull requests or issues with improvements and bug fixes.
1361
+ We welcome contributions to the Star DB Query Builder! Here's how you can help:
1362
+
1363
+ ### Development Setup
1364
+
1365
+ 1. **Clone the repository**
1366
+
1367
+ ```bash
1368
+ git clone https://github.com/starbem/star-db-query-builder.git
1369
+ cd star-db-query-builder
1370
+ ```
1371
+
1372
+ 2. **Install dependencies**
1373
+
1374
+ ```bash
1375
+ pnpm install
1376
+ ```
1377
+
1378
+ 3. **Run tests**
1379
+
1380
+ ```bash
1381
+ pnpm test
1382
+ ```
1383
+
1384
+ 4. **Run linting**
1385
+
1386
+ ```bash
1387
+ pnpm lint
1388
+ ```
1389
+
1390
+ 5. **Build the project**
1391
+ ```bash
1392
+ pnpm build
1393
+ ```
1394
+
1395
+ ### Contributing Guidelines
1396
+
1397
+ - **Code Style**: Follow the existing code style and use Prettier for formatting
1398
+ - **TypeScript**: Maintain strict TypeScript typing
1399
+ - **Tests**: Add tests for new features and bug fixes
1400
+ - **Documentation**: Update documentation for any API changes
1401
+ - **Commit Messages**: Use conventional commit messages
1402
+
1403
+ ### Pull Request Process
1404
+
1405
+ 1. Fork the repository
1406
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
1407
+ 3. Make your changes
1408
+ 4. Add tests for your changes
1409
+ 5. Ensure all tests pass (`pnpm test`)
1410
+ 6. Run linting (`pnpm lint`)
1411
+ 7. Commit your changes (`git commit -m 'feat: add amazing feature'`)
1412
+ 8. Push to your branch (`git push origin feature/amazing-feature`)
1413
+ 9. Open a Pull Request
1414
+
1415
+ ### Reporting Issues
1416
+
1417
+ When reporting issues, please include:
1418
+
1419
+ - **Environment**: Node.js version, database type and version
1420
+ - **Steps to Reproduce**: Clear steps to reproduce the issue
1421
+ - **Expected Behavior**: What you expected to happen
1422
+ - **Actual Behavior**: What actually happened
1423
+ - **Code Sample**: Minimal code sample that demonstrates the issue
1424
+
1425
+ ### Feature Requests
1426
+
1427
+ For feature requests, please:
1428
+
1429
+ - **Describe the feature**: Clear description of what you want
1430
+ - **Use Case**: Explain why this feature would be useful
1431
+ - **Proposed API**: If you have ideas for the API design
1432
+ - **Alternatives**: Any alternative solutions you've considered
376
1433
 
377
1434
  ## License
378
1435
 
379
- This project is licensed under the MIT License.
1436
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1437
+
1438
+ ## Support
1439
+
1440
+ - **Documentation**: [GitHub Wiki](https://github.com/starbem/star-db-query-builder/wiki)
1441
+ - **Issues**: [GitHub Issues](https://github.com/starbem/star-db-query-builder/issues)
1442
+ - **Discussions**: [GitHub Discussions](https://github.com/starbem/star-db-query-builder/discussions)
1443
+
1444
+ ## Changelog
380
1445
 
381
- This documentation explains how to set up connections with external configuration for both connection pool and retry options, retrieve clients to execute queries, and monitor events for logging and diagnostics.
1446
+ See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.
382
1447
 
383
1448
  ---
384
1449
 
385
- 💻 Happy Coding!
1450
+ Made with ❤️ by the Starbem team