pg-query-sdk 1.0.0 → 1.0.2
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 +99 -370
- package/dist/cjs/builders/ConditionBuilder.d.ts +34 -0
- package/dist/cjs/builders/ConditionBuilder.js +34 -0
- package/dist/cjs/builders/QueryBuilder.d.ts +112 -1
- package/dist/cjs/builders/QueryBuilder.js +94 -1
- package/dist/cjs/core/Database.d.ts +26 -0
- package/dist/cjs/core/Database.js +26 -0
- package/dist/cjs/core/ParamContext.d.ts +16 -0
- package/dist/cjs/core/ParamContext.js +16 -0
- package/dist/cjs/core/QueryExecutor.d.ts +26 -0
- package/dist/cjs/core/QueryExecutor.js +26 -0
- package/dist/cjs/core/TransactionManager.d.ts +13 -0
- package/dist/cjs/core/TransactionManager.js +13 -0
- package/dist/cjs/core/UnitOfWork.d.ts +19 -0
- package/dist/cjs/core/UnitOfWork.js +19 -0
- package/dist/cjs/dialects/Dialect.d.ts +13 -0
- package/dist/cjs/dialects/MysqlDialect.d.ts +12 -0
- package/dist/cjs/dialects/MysqlDialect.js +12 -0
- package/dist/cjs/dialects/PostgresDialect.d.ts +13 -0
- package/dist/cjs/dialects/PostgresDialect.js +13 -0
- package/dist/cjs/index.d.ts +28 -0
- package/dist/cjs/index.js +28 -0
- package/dist/cjs/orm/Repository.d.ts +34 -0
- package/dist/cjs/orm/Repository.js +34 -0
- package/dist/cjs/query/ConditionBuilder.d.ts +33 -0
- package/dist/cjs/query/ConditionBuilder.js +33 -0
- package/dist/cjs/query/QueryBuilder.d.ts +154 -0
- package/dist/cjs/query/QueryBuilder.js +136 -0
- package/dist/esm/builders/ConditionBuilder.d.ts +34 -0
- package/dist/esm/builders/ConditionBuilder.js +34 -0
- package/dist/esm/builders/QueryBuilder.d.ts +112 -1
- package/dist/esm/builders/QueryBuilder.js +94 -1
- package/dist/esm/core/Database.d.ts +26 -0
- package/dist/esm/core/Database.js +26 -0
- package/dist/esm/core/ParamContext.d.ts +16 -0
- package/dist/esm/core/ParamContext.js +16 -0
- package/dist/esm/core/QueryExecutor.d.ts +26 -0
- package/dist/esm/core/QueryExecutor.js +26 -0
- package/dist/esm/core/TransactionManager.d.ts +13 -0
- package/dist/esm/core/TransactionManager.js +13 -0
- package/dist/esm/core/UnitOfWork.d.ts +19 -0
- package/dist/esm/core/UnitOfWork.js +19 -0
- package/dist/esm/dialects/Dialect.d.ts +13 -0
- package/dist/esm/dialects/MysqlDialect.d.ts +12 -0
- package/dist/esm/dialects/MysqlDialect.js +12 -0
- package/dist/esm/dialects/PostgresDialect.d.ts +13 -0
- package/dist/esm/dialects/PostgresDialect.js +13 -0
- package/dist/esm/index.d.ts +28 -0
- package/dist/esm/index.js +28 -0
- package/dist/esm/orm/Repository.d.ts +34 -0
- package/dist/esm/orm/Repository.js +34 -0
- package/dist/esm/query/ConditionBuilder.d.ts +33 -0
- package/dist/esm/query/ConditionBuilder.js +33 -0
- package/dist/esm/query/QueryBuilder.d.ts +154 -0
- package/dist/esm/query/QueryBuilder.js +136 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,100 +1,69 @@
|
|
|
1
|
-
# PG
|
|
1
|
+
# PG Query SDK (TypeScript)
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://www.typescriptlang.
|
|
5
|
-
[](#-arquitetura)
|
|
4
|
+
[](https://www.typescriptlang.com/)
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
## A Robust and Type-Safe PostgreSQL Integration Library for TypeScript
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
The PG Query SDK is a comprehensive TypeScript library designed to facilitate seamless and type-safe interaction with PostgreSQL databases. It provides a structured approach to query construction, execution, and transaction management, enhancing developer productivity and reducing common database-related errors.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
### Key Features:
|
|
11
|
+
|
|
12
|
+
* **`Database`**: The central entry point for database operations, managing connections and configurations.
|
|
13
|
+
* **`QueryBuilder`**: A fluent interface for constructing complex SQL `SELECT` queries programmatically.
|
|
14
|
+
* **`ConditionBuilder`**: Specialized builder for crafting intricate `WHERE` and `HAVING` clauses.
|
|
15
|
+
* **`QueryExecutor`**: Manages database connection pooling and executes raw SQL queries securely.
|
|
16
|
+
* **Basic ORM Capabilities**: Includes an abstract `Repository` class for data access abstraction, with extensible methods for DML operations.
|
|
17
|
+
* **Transaction Management**: Robust support for ACID-compliant database transactions.
|
|
19
18
|
|
|
20
19
|
---
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
## 📦 Installation
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
npm install pg-query-sdk
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Ou localmente:
|
|
23
|
+
To integrate the PG Query SDK into your project, execute the following command:
|
|
29
24
|
|
|
30
25
|
```bash
|
|
31
|
-
npm install
|
|
26
|
+
npm install pg-query-sdk
|
|
32
27
|
```
|
|
33
28
|
|
|
34
29
|
---
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
## 🚀 Getting Started
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
### Initializing the Database
|
|
39
34
|
|
|
40
|
-
|
|
35
|
+
The `Database` class serves as the primary interface for all database interactions. Instantiate it with your PostgreSQL connection string.
|
|
41
36
|
|
|
42
37
|
```typescript
|
|
43
|
-
import Database from 'pg-query-sdk';
|
|
38
|
+
import {Database} from 'pg-query-sdk';
|
|
44
39
|
|
|
45
40
|
const db = new Database({
|
|
46
41
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
47
|
-
// Opcional: Você pode especificar um dialeto diferente se necessário
|
|
48
|
-
// dialect: new MyCustomDialect(),
|
|
49
|
-
// Opcional: TTL padrão para cache de queries (em segundos). Use 0 para desativar.
|
|
50
|
-
// defaultCacheTTL: 300
|
|
51
42
|
});
|
|
52
|
-
|
|
53
|
-
// Agora 'db' está pronto para ser usado!
|
|
54
43
|
```
|
|
55
44
|
|
|
56
45
|
---
|
|
57
46
|
|
|
58
|
-
|
|
47
|
+
## 🛠 Core Functionalities
|
|
59
48
|
|
|
60
|
-
|
|
49
|
+
### 1️⃣ QueryBuilder: Fluent SQL `SELECT` Query Construction
|
|
61
50
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Ele **não executa nada**, apenas retorna a string SQL e os parâmetros. Para executar a query, você deve encadear `.execute()` no final.
|
|
65
|
-
|
|
66
|
-
### Selecionando Dados
|
|
51
|
+
The `QueryBuilder` enables the programmatic construction of SQL `SELECT` statements, accessible via the `db.table()` method.
|
|
67
52
|
|
|
68
53
|
```typescript
|
|
69
|
-
import Database from 'pg-query-sdk';
|
|
54
|
+
import {Database} from 'pg-query-sdk';
|
|
70
55
|
|
|
71
56
|
const db = new Database({
|
|
72
57
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
73
58
|
});
|
|
74
59
|
|
|
75
60
|
async function selectExample() {
|
|
76
|
-
// Construindo a query
|
|
77
|
-
const { query, params } = db.table('users')
|
|
78
|
-
.select(['id', 'name', 'email'])
|
|
79
|
-
.where({ active: true })
|
|
80
|
-
.limit(10)
|
|
81
|
-
.offset(0)
|
|
82
|
-
.orderBy('name', 'ASC')
|
|
83
|
-
.build(); // Apenas constrói a query, não executa
|
|
84
|
-
|
|
85
|
-
console.log('SELECT Query:', query);
|
|
86
|
-
// Ex: SELECT id, name, email FROM users WHERE active = $1 ORDER BY name ASC LIMIT 10 OFFSET 0
|
|
87
|
-
console.log('SELECT Params:', params);
|
|
88
|
-
// Ex: [true]
|
|
89
|
-
|
|
90
|
-
// Executando a query
|
|
91
61
|
const users = await db.table('users')
|
|
92
62
|
.select(['id', 'name', 'email'])
|
|
93
63
|
.where({ active: true })
|
|
94
64
|
.limit(10)
|
|
95
|
-
.offset(0)
|
|
96
65
|
.orderBy('name', 'ASC')
|
|
97
|
-
.execute();
|
|
66
|
+
.execute();
|
|
98
67
|
|
|
99
68
|
console.log('Selected Users:', users);
|
|
100
69
|
}
|
|
@@ -102,122 +71,12 @@ async function selectExample() {
|
|
|
102
71
|
selectExample();
|
|
103
72
|
```
|
|
104
73
|
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
import Database from 'pg-query-sdk';
|
|
109
|
-
|
|
110
|
-
const db = new Database({
|
|
111
|
-
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
async function joinExample() {
|
|
115
|
-
const usersWithOrders = await db.table('users')
|
|
116
|
-
.select(['users.name', 'orders.amount', 'orders.status'])
|
|
117
|
-
.join('orders', 'users.id', 'orders.user_id') // INNER JOIN
|
|
118
|
-
.where({ 'orders.status': 'completed' })
|
|
119
|
-
.execute();
|
|
120
|
-
|
|
121
|
-
console.log('Users with completed orders:', usersWithOrders);
|
|
122
|
-
|
|
123
|
-
const usersAndTheirOrders = await db.table('users')
|
|
124
|
-
.select(['users.name', 'orders.amount', 'orders.status'])
|
|
125
|
-
.leftJoin('orders', 'users.id', 'orders.user_id') // LEFT JOIN
|
|
126
|
-
.orderBy('users.name', 'ASC')
|
|
127
|
-
.execute();
|
|
128
|
-
|
|
129
|
-
console.log('Users and all their orders (if any):', usersAndTheirOrders);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
joinExample();
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Group By e Having
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
import Database from 'pg-query-sdk';
|
|
139
|
-
|
|
140
|
-
const db = new Database({
|
|
141
|
-
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
142
|
-
});
|
|
74
|
+
### 2️⃣ ConditionBuilder: Advanced `WHERE` and `HAVING` Clauses
|
|
143
75
|
|
|
144
|
-
|
|
145
|
-
const categorySales = await db.table('products')
|
|
146
|
-
.select(['category', 'COUNT(id) as total_products', 'SUM(price) as total_value'])
|
|
147
|
-
.groupBy('category')
|
|
148
|
-
.having({ 'SUM(price)': { op: '>', value: 1000 } }) // HAVING SUM(price) > 1000
|
|
149
|
-
.orderBy('total_value', 'DESC')
|
|
150
|
-
.execute();
|
|
151
|
-
|
|
152
|
-
console.log('Category sales over 1000:', categorySales);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
groupByHavingExample();
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Common Table Expressions (CTEs)
|
|
76
|
+
The `ConditionBuilder` facilitates the creation of complex conditional logic within `WHERE` and `HAVING` clauses, typically used in conjunction with the `QueryBuilder`.
|
|
159
77
|
|
|
160
78
|
```typescript
|
|
161
|
-
import Database from 'pg-query-sdk';
|
|
162
|
-
|
|
163
|
-
const db = new Database({
|
|
164
|
-
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
async function cteExample() {
|
|
168
|
-
// Subquery para usuários ativos
|
|
169
|
-
const activeUsersSubquery = db.table('users')
|
|
170
|
-
.select(['id', 'name'])
|
|
171
|
-
.where({ active: true });
|
|
172
|
-
|
|
173
|
-
// Query principal usando a CTE
|
|
174
|
-
const result = await db.table('active_users') // Referencia a CTE pelo nome
|
|
175
|
-
.with('active_users', activeUsersSubquery) // Define a CTE
|
|
176
|
-
.select(['name'])
|
|
177
|
-
.orderBy('name', 'ASC')
|
|
178
|
-
.execute();
|
|
179
|
-
|
|
180
|
-
console.log('Users from CTE:', result);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
cteExample();
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Subqueries na Cláusula FROM
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import Database from 'pg-query-sdk';
|
|
190
|
-
|
|
191
|
-
const db = new Database({
|
|
192
|
-
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
async function fromSubqueryExample() {
|
|
196
|
-
// Subquery para obter o total de pedidos por usuário
|
|
197
|
-
const userOrderCounts = db.table('orders')
|
|
198
|
-
.select(['user_id', 'COUNT(id) as order_count'])
|
|
199
|
-
.groupBy('user_id');
|
|
200
|
-
|
|
201
|
-
// Query principal usando a subquery como tabela
|
|
202
|
-
const usersWithOrderCounts = await db.table('users')
|
|
203
|
-
.select(['users.name', 'uoc.order_count'])
|
|
204
|
-
.fromSubquery(userOrderCounts, 'uoc') // Usa a subquery 'userOrderCounts' como 'uoc'
|
|
205
|
-
.join('users', 'users.id', 'uoc.user_id') // JOIN com a tabela original de usuários
|
|
206
|
-
.where({ 'uoc.order_count': { op: '>', value: 5 } })
|
|
207
|
-
.execute();
|
|
208
|
-
|
|
209
|
-
console.log('Users with more than 5 orders:', usersWithOrderCounts);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
fromSubqueryExample();
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
## 2️⃣ ConditionBuilder: Cláusulas WHERE e HAVING Avançadas
|
|
216
|
-
|
|
217
|
-
O `ConditionBuilder` é usado dentro dos métodos `where()` e `having()` do `QueryBuilder` para construir condições complexas, incluindo operadores, `NULL` checks, expressões raw e agrupamentos `AND`/`OR`.
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
import Database from 'pg-query-sdk';
|
|
79
|
+
import {Database} from 'pg-query-sdk';
|
|
221
80
|
|
|
222
81
|
const db = new Database({
|
|
223
82
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
@@ -225,72 +84,52 @@ const db = new Database({
|
|
|
225
84
|
|
|
226
85
|
async function complexWhereExample() {
|
|
227
86
|
const products = await db.table('products')
|
|
228
|
-
.select(['name', 'price'
|
|
87
|
+
.select(['name', 'price'])
|
|
229
88
|
.where(conditions => {
|
|
230
89
|
conditions
|
|
231
|
-
.where({ category: 'electronics' })
|
|
232
|
-
.
|
|
233
|
-
|
|
234
|
-
.where({
|
|
235
|
-
.
|
|
236
|
-
|
|
237
|
-
.where({ price: { op: '<', value: 100 } }) // price < $3
|
|
238
|
-
.raw('created_at > NOW() - INTERVAL \'1 year\''); // created_at > ...
|
|
239
|
-
});
|
|
240
|
-
})
|
|
241
|
-
.where({ manufacturer: null }); // manufacturer IS NULL
|
|
90
|
+
.where({ category: 'electronics' })
|
|
91
|
+
.orGroup(group => {
|
|
92
|
+
group
|
|
93
|
+
.where({ price: { op: '<', value: 100 } })
|
|
94
|
+
.where({ stock: { op: '>', value: 0 } });
|
|
95
|
+
});
|
|
242
96
|
})
|
|
243
97
|
.execute();
|
|
244
98
|
|
|
245
99
|
console.log('Complex WHERE Products:', products);
|
|
246
|
-
// A query gerada seria algo como:
|
|
247
|
-
// SELECT name, price, stock, category FROM products
|
|
248
|
-
// WHERE category = $1 AND (stock > $2 OR (price < $3 AND created_at > NOW() - INTERVAL '1 year')) AND manufacturer IS NULL
|
|
249
100
|
}
|
|
250
101
|
|
|
251
102
|
complexWhereExample();
|
|
252
103
|
```
|
|
253
104
|
|
|
254
|
-
|
|
105
|
+
### 3️⃣ QueryExecutor: Direct Query Execution
|
|
255
106
|
|
|
256
|
-
|
|
107
|
+
For scenarios requiring direct execution of raw SQL queries, the `QueryExecutor` can be instantiated and utilized.
|
|
257
108
|
|
|
258
109
|
```typescript
|
|
259
|
-
import
|
|
110
|
+
import { QueryExecutor } from 'pg-query-sdk';
|
|
260
111
|
|
|
261
|
-
const
|
|
112
|
+
const executor = new QueryExecutor({
|
|
262
113
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
263
114
|
});
|
|
264
115
|
|
|
265
116
|
async function directExecuteExample() {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
'SELECT version(), NOW() as current_time',
|
|
117
|
+
const result = await executor.execute(
|
|
118
|
+
'SELECT version()',
|
|
269
119
|
[]
|
|
270
120
|
);
|
|
271
121
|
console.log('Direct Execution Result:', result.rows);
|
|
272
|
-
|
|
273
|
-
// Executando uma query com parâmetros
|
|
274
|
-
const specificUser = await db.executor.execute(
|
|
275
|
-
'SELECT * FROM users WHERE id = $1',
|
|
276
|
-
[1]
|
|
277
|
-
);
|
|
278
|
-
console.log('Specific User (Direct):', specificUser.rows[0]);
|
|
279
|
-
|
|
280
|
-
// Exemplo de DDL (Data Definition Language) - CUIDADO ao usar em produção!
|
|
281
|
-
// await db.executor.execute('CREATE TABLE IF NOT EXISTS temp_table (id SERIAL PRIMARY KEY, name VARCHAR(255))', []);
|
|
282
|
-
// console.log('temp_table created (if not exists).');
|
|
283
122
|
}
|
|
284
123
|
|
|
285
124
|
directExecuteExample();
|
|
286
125
|
```
|
|
287
126
|
|
|
288
|
-
|
|
127
|
+
### 4️⃣ ACID Transactions
|
|
289
128
|
|
|
290
|
-
|
|
129
|
+
The SDK provides robust support for managing ACID-compliant transactions, ensuring data integrity.
|
|
291
130
|
|
|
292
131
|
```typescript
|
|
293
|
-
import Database from 'pg-query-sdk';
|
|
132
|
+
import {Database} from 'pg-query-sdk';
|
|
294
133
|
|
|
295
134
|
const db = new Database({
|
|
296
135
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
@@ -299,255 +138,145 @@ const db = new Database({
|
|
|
299
138
|
async function transactionExample() {
|
|
300
139
|
try {
|
|
301
140
|
const result = await db.transaction(async trxDb => {
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// Todas as operações feitas com 'trxDb' farão parte da mesma transação.
|
|
305
|
-
|
|
306
|
-
// Exemplo: Transferir fundos entre contas
|
|
307
|
-
const senderId = 1;
|
|
308
|
-
const receiverId = 2;
|
|
309
|
-
const amount = 100.00;
|
|
310
|
-
|
|
311
|
-
// 1. Decrementar saldo do remetente
|
|
312
|
-
// Nota: Para INSERT/UPDATE/DELETE, você precisará usar db.executor.execute() diretamente
|
|
313
|
-
// ou implementar esses métodos em um repositório customizado.
|
|
141
|
+
// 'trxDb' is a Database instance bound to the current transaction.
|
|
142
|
+
// Use 'trxDb.executor.execute()' for DML operations within the transaction.
|
|
314
143
|
await trxDb.executor.execute(
|
|
315
|
-
'UPDATE accounts SET balance = balance - $1 WHERE id = $2
|
|
316
|
-
[
|
|
144
|
+
'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
|
|
145
|
+
[100.00, 1]
|
|
317
146
|
);
|
|
318
|
-
console.log(`Decremented balance for account ${senderId}`);
|
|
319
|
-
|
|
320
|
-
// Simular uma falha para testar o rollback
|
|
321
|
-
// if (true) throw new Error('Simulated failure');
|
|
322
|
-
|
|
323
|
-
// 2. Incrementar saldo do destinatário
|
|
324
147
|
await trxDb.executor.execute(
|
|
325
|
-
'UPDATE accounts SET balance = balance + $1 WHERE id = $2
|
|
326
|
-
[
|
|
148
|
+
'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
|
|
149
|
+
[100.00, 2]
|
|
327
150
|
);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
// Se tudo ocorrer bem, a transação será commitada automaticamente.
|
|
331
|
-
return `Transaction successful: ${amount} transferred from ${senderId} to ${receiverId}`;
|
|
151
|
+
return 'Transaction completed successfully.';
|
|
332
152
|
});
|
|
333
|
-
|
|
334
153
|
console.log(result);
|
|
335
|
-
} catch (error) {
|
|
154
|
+
} catch (error: any) {
|
|
336
155
|
console.error('Transaction failed:', error.message);
|
|
337
|
-
// Se uma exceção for lançada, a transação será automaticamente revertida (rollback).
|
|
338
156
|
}
|
|
339
157
|
}
|
|
340
158
|
|
|
341
159
|
transactionExample();
|
|
342
160
|
```
|
|
343
161
|
|
|
344
|
-
|
|
162
|
+
### 5️⃣ Basic ORM with Repositories
|
|
345
163
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
### Definindo um Repositório Customizado
|
|
349
|
-
|
|
350
|
-
A classe base `Repository<T>` oferece um método `findById` e um `qb()` que retorna um `QueryBuilder` pré-configurado para a tabela. Para operações de `INSERT`, `UPDATE` e `DELETE`, você precisará implementá-las em seus repositórios customizados, utilizando o `QueryExecutor` ou o `QueryBuilder` (para `SELECT` após a operação).
|
|
164
|
+
The abstract `Repository<T>` class offers a foundational ORM layer, providing methods like `findById` and a pre-configured `QueryBuilder` (`qb()`). Custom DML operations (`insert`, `update`, `delete`) should be implemented in concrete repository classes.
|
|
351
165
|
|
|
352
166
|
```typescript
|
|
353
|
-
import { Repository
|
|
167
|
+
import {Database, Repository } from 'pg-query-sdk';
|
|
168
|
+
import { QueryExecutor, Dialect } from 'pg-query-sdk';
|
|
354
169
|
|
|
355
|
-
// 1. Defina a interface para sua entidade
|
|
356
170
|
interface User {
|
|
357
171
|
id: number;
|
|
358
172
|
name: string;
|
|
359
173
|
email: string;
|
|
360
|
-
age?: number;
|
|
361
|
-
active: boolean;
|
|
362
174
|
}
|
|
363
175
|
|
|
364
|
-
// 2. Crie sua classe de repositório estendendo Repository<T>
|
|
365
176
|
class UserRepository extends Repository<User> {
|
|
366
177
|
constructor(executor: QueryExecutor, dialect: Dialect) {
|
|
367
|
-
// O construtor base requer o nome da tabela, o executor e o dialeto
|
|
368
178
|
super('users', executor, dialect);
|
|
369
179
|
}
|
|
370
180
|
|
|
371
|
-
|
|
372
|
-
// async findById(id: number): Promise<User | null> { ... }
|
|
373
|
-
|
|
374
|
-
// Exemplo de método customizado para o repositório de usuários
|
|
375
|
-
async findActiveUsers(): Promise<User[]> {
|
|
376
|
-
return this.qb() // 'this.qb()' retorna um QueryBuilder pré-configurado para a tabela 'users'
|
|
377
|
-
.where({ active: true })
|
|
378
|
-
.execute();
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
async findUsersByAgeRange(minAge: number, maxAge: number): Promise<User[]> {
|
|
181
|
+
async findByName(name: string): Promise<User[]> {
|
|
382
182
|
return this.qb()
|
|
383
|
-
.where(
|
|
384
|
-
conditions
|
|
385
|
-
.where({ age: { op: '>=', value: minAge } })
|
|
386
|
-
.where({ age: { op: '<=', value: maxAge } });
|
|
387
|
-
})
|
|
183
|
+
.where({ name })
|
|
388
184
|
.execute();
|
|
389
185
|
}
|
|
390
186
|
|
|
391
|
-
//
|
|
392
|
-
async
|
|
393
|
-
const { query, params } = this.dialect.createInsertQuery(this.table, data as Record<string, any>, ['id', 'name', 'email', 'age', 'active']);
|
|
394
|
-
const result = await this.executor.execute(query, params);
|
|
395
|
-
return result.rows[0];
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Exemplo de implementação de UPDATE em um repositório customizado
|
|
399
|
-
async updateUser(id: number, data: Partial<User>): Promise<User | null> {
|
|
400
|
-
const { query, params } = this.dialect.createUpdateQuery(this.table, data as Record<string, any>, { id }, ['id', 'name', 'email', 'age', 'active']);
|
|
401
|
-
const result = await this.executor.execute(query, params);
|
|
402
|
-
return result.rows[0] || null;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Exemplo de implementação de DELETE em um repositório customizado
|
|
406
|
-
async deleteUser(id: number): Promise<boolean> {
|
|
407
|
-
const { query, params } = this.dialect.createDeleteQuery(this.table, { id });
|
|
408
|
-
const result = await this.executor.execute(query, params);
|
|
409
|
-
return result.rowCount > 0;
|
|
410
|
-
}
|
|
187
|
+
// Implement DML operations as needed.
|
|
188
|
+
// Example: async insert(data: Partial<User>): Promise<User> { /* ... */ }
|
|
411
189
|
}
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### Usando o Repositório Customizado
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
import Database from 'pg-query-sdk';
|
|
418
|
-
// Importe seu UserRepository definido acima
|
|
419
|
-
import { UserRepository } from './path/to/UserRepository'; // Ajuste o caminho conforme necessário
|
|
420
190
|
|
|
421
191
|
const db = new Database({
|
|
422
192
|
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
423
193
|
});
|
|
424
194
|
|
|
425
195
|
async function repositoryExample() {
|
|
426
|
-
// Obtenha uma instância do seu repositório através do método .repository() do Database
|
|
427
196
|
const userRepository = db.repository(UserRepository);
|
|
428
197
|
|
|
429
|
-
// Usando métodos do repositório base
|
|
430
198
|
const userById = await userRepository.findById(1);
|
|
431
199
|
console.log('User by ID:', userById);
|
|
432
200
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
console.log('Active Users:', activeUsers);
|
|
436
|
-
|
|
437
|
-
const usersInAgeRange = await userRepository.findUsersByAgeRange(25, 35);
|
|
438
|
-
console.log('Users in age range 25-35:', usersInAgeRange);
|
|
439
|
-
|
|
440
|
-
// Criando um novo usuário
|
|
441
|
-
const newUser = await userRepository.createUser({
|
|
442
|
-
name: 'Charlie',
|
|
443
|
-
email: 'charlie@example.com',
|
|
444
|
-
age: 29,
|
|
445
|
-
active: true
|
|
446
|
-
});
|
|
447
|
-
console.log('Created new user:', newUser);
|
|
448
|
-
|
|
449
|
-
// Atualizando um usuário
|
|
450
|
-
const updatedUser = await userRepository.updateUser(newUser.id, { age: 30, active: false });
|
|
451
|
-
console.log('Updated user:', updatedUser);
|
|
452
|
-
|
|
453
|
-
// Deletando um usuário
|
|
454
|
-
const deleted = await userRepository.deleteUser(newUser.id);
|
|
455
|
-
console.log(`User ${newUser.id} deleted: ${deleted}`);
|
|
201
|
+
const usersByName = await userRepository.findByName('Alice');
|
|
202
|
+
console.log('Users by Name:', usersByName);
|
|
456
203
|
}
|
|
457
204
|
|
|
458
205
|
repositoryExample();
|
|
459
206
|
```
|
|
460
207
|
|
|
461
|
-
### EntityManager (Planejado)
|
|
462
|
-
|
|
463
|
-
O `EntityManager` é um componente planejado para gerenciar múltiplos repositórios e unidades de trabalho, oferecendo uma interface centralizada para operações de persistência mais complexas. Atualmente, esta classe está vazia e será desenvolvida em futuras iterações.
|
|
464
|
-
|
|
465
208
|
---
|
|
466
209
|
|
|
467
|
-
|
|
210
|
+
## ⚙️ Dual Module Support
|
|
468
211
|
|
|
469
|
-
|
|
212
|
+
The package provides support for both CommonJS and ES Modules environments.
|
|
470
213
|
|
|
471
|
-
|
|
214
|
+
### CommonJS
|
|
472
215
|
|
|
473
216
|
```javascript
|
|
474
|
-
const Database = require('pg-query-sdk').default;
|
|
475
|
-
|
|
217
|
+
const Database = require('pg-query-sdk').default;
|
|
476
218
|
const db = new Database({ /* ... */ });
|
|
477
219
|
```
|
|
478
220
|
|
|
479
|
-
|
|
221
|
+
### ESM
|
|
480
222
|
|
|
481
223
|
```typescript
|
|
482
|
-
import Database from 'pg-query-sdk';
|
|
483
|
-
|
|
224
|
+
import {Database} from 'pg-query-sdk';
|
|
484
225
|
const db = new Database({ /* ... */ });
|
|
485
226
|
```
|
|
486
227
|
|
|
487
|
-
Isso funciona graças ao campo `exports` no `package.json`:
|
|
488
|
-
|
|
489
|
-
```json
|
|
490
|
-
{
|
|
491
|
-
"exports": {
|
|
492
|
-
".": {
|
|
493
|
-
"require": "./dist/cjs/index.js",
|
|
494
|
-
"import": "./dist/esm/index.js"
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
228
|
---
|
|
501
229
|
|
|
502
|
-
|
|
230
|
+
## 🧱 Project Structure
|
|
231
|
+
|
|
232
|
+
The project is organized into distinct modules, each responsible for a specific aspect of database interaction:
|
|
503
233
|
|
|
504
234
|
```
|
|
505
235
|
pg-query-sdk/
|
|
506
236
|
src/
|
|
507
237
|
core/
|
|
508
|
-
Database.ts #
|
|
509
|
-
ParamContext.ts #
|
|
510
|
-
QueryExecutor.ts #
|
|
511
|
-
TransactionManager.ts #
|
|
238
|
+
Database.ts # Central database interface
|
|
239
|
+
ParamContext.ts # Manages query parameters
|
|
240
|
+
QueryExecutor.ts # Executes SQL queries
|
|
241
|
+
TransactionManager.ts # Handles database transactions
|
|
512
242
|
builders/
|
|
513
|
-
ConditionBuilder.ts #
|
|
514
|
-
QueryBuilder.ts #
|
|
243
|
+
ConditionBuilder.ts # Builds WHERE/HAVING clauses
|
|
244
|
+
QueryBuilder.ts # Builds SELECT queries
|
|
515
245
|
orm/
|
|
516
|
-
EntityManager.ts # (
|
|
517
|
-
Repository.ts #
|
|
246
|
+
EntityManager.ts # (Planned) Entity management
|
|
247
|
+
Repository.ts # Abstract base for data access
|
|
518
248
|
dialects/
|
|
519
|
-
Dialect.ts # Interface
|
|
520
|
-
PostgresDialect.ts #
|
|
521
|
-
index.ts #
|
|
522
|
-
test/ # Testes unitários e de integração
|
|
523
|
-
dist/ # Saída da compilação (CJS e ESM)
|
|
249
|
+
Dialect.ts # Interface for database dialects
|
|
250
|
+
PostgresDialect.ts # PostgreSQL specific dialect implementation
|
|
251
|
+
index.ts # Main entry point for module exports
|
|
524
252
|
```
|
|
525
253
|
|
|
526
254
|
---
|
|
527
255
|
|
|
528
|
-
|
|
256
|
+
## 📌 Responsibilities of Layers
|
|
529
257
|
|
|
530
|
-
|
|
|
531
|
-
|
|
532
|
-
| `Database`
|
|
533
|
-
| `QueryBuilder`
|
|
534
|
-
| `ConditionBuilder`
|
|
535
|
-
| `QueryExecutor`
|
|
536
|
-
| `Repository`
|
|
537
|
-
| `TransactionManager
|
|
538
|
-
| `EntityManager`
|
|
539
|
-
| `pg` (driver) | Comunicação de baixo nível com o banco de dados PostgreSQL. |
|
|
258
|
+
| Layer | Responsibility |
|
|
259
|
+
| :-------------------- | :------------------------------------------------------------------------------------------------------------- |
|
|
260
|
+
| `Database` | Serves as the primary entry point, managing connection configurations, dialect, transactions, and access to builders and repositories. |
|
|
261
|
+
| `QueryBuilder` | Provides a fluent API for constructing SQL `SELECT` queries. |
|
|
262
|
+
| `ConditionBuilder` | Facilitates the construction of complex `WHERE` and `HAVING` clauses. |
|
|
263
|
+
| `QueryExecutor` | Manages the PostgreSQL connection pool and executes SQL queries, ensuring resource efficiency. |
|
|
264
|
+
| `Repository` | Offers an abstract layer for data access operations for a specific entity. Includes `findById` and a pre-configured `QueryBuilder`. Custom DML operations are to be implemented by concrete classes. |
|
|
265
|
+
| `TransactionManager` | Orchestrates ACID-compliant database transactions, ensuring atomicity, consistency, isolation, and durability. |
|
|
266
|
+
| `EntityManager` | (Planned) Will manage multiple repositories and coordinate units of work for complex persistence scenarios. |
|
|
540
267
|
|
|
541
268
|
---
|
|
542
269
|
|
|
543
|
-
|
|
270
|
+
## 🔐 Security Considerations
|
|
271
|
+
|
|
272
|
+
The PG Query SDK prioritizes security through several mechanisms:
|
|
544
273
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
274
|
+
* **Prepared Statements**: All queries utilize prepared statements with parameterized values, effectively mitigating SQL injection vulnerabilities.
|
|
275
|
+
* **Connection Pooling**: The `QueryExecutor` employs connection pooling to manage database connections efficiently and securely, reducing overhead and preventing resource exhaustion.
|
|
276
|
+
* **Resource Management**: Database connections are meticulously released in `finally` blocks within transaction and execution contexts, preventing connection leaks and ensuring system stability.
|
|
548
277
|
|
|
549
278
|
---
|
|
550
279
|
|
|
551
|
-
## 📄
|
|
280
|
+
## 📄 License
|
|
552
281
|
|
|
553
|
-
|
|
282
|
+
This project is distributed under the MIT License. For more details, please refer to the `LICENSE` file.
|