@groundbrick/db-postgres 0.0.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.
package/README.md ADDED
@@ -0,0 +1,747 @@
1
+ # @groundbrick/db-postgres
2
+
3
+ Cliente PostgreSQL para o microframework TypeScript com connection pooling, monitoramento de saúde, gerenciamento de transações e sistema de migrações completo.
4
+
5
+ ## Features Implementadas
6
+
7
+ - **🔗 Connection Pooling** - Usando `pg` (node-postgres) com configuração avançada
8
+ - **🏭 Singleton Factory** - Gerenciamento centralizado de instâncias de clientes
9
+ - **🔄 Transaction Management** - Transações automáticas e manuais com BaseTransaction
10
+ - **📊 Health Monitoring** - Verificações de saúde com métricas de timing
11
+ - **🗃️ Migration System** - Sistema completo com UP/DOWN scripts e checksum
12
+ - **⚙️ TypeScript Support** - Tipagem completa para queries e configurações
13
+ - **🛡️ Error Handling** - Tipos de erro específicos (ConnectionError, QueryError, etc.)
14
+ - **📈 Pool Monitoring** - Informações detalhadas sobre estado do pool de conexões
15
+
16
+ ## Instalação
17
+
18
+ ```bash
19
+ npm install @groundbrick/db-postgres
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ ```bash
25
+ npm install pg
26
+ npm install --save-dev @types/pg
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Configuração Básica
32
+
33
+ ```typescript
34
+ // src/database.ts
35
+ import { PostgresFactory, PostgresConfig } from '@groundbrick/db-postgres';
36
+ import { createLogger } from '@groundbrick/logger';
37
+
38
+ const config: PostgresConfig = {
39
+ host: process.env.DB_HOST || 'localhost',
40
+ port: parseInt(process.env.DB_PORT || '5432'),
41
+ database: process.env.DB_NAME || 'myapp',
42
+ user: process.env.DB_USER || 'postgres',
43
+ password: process.env.DB_PASSWORD || 'secret',
44
+
45
+ // Pool configuration
46
+ max: 10,
47
+ min: 2,
48
+ connectionTimeoutMillis: 60000,
49
+ idleTimeoutMillis: 30000,
50
+
51
+ // SSL configuration (production)
52
+ ssl: process.env.NODE_ENV === 'production' ? {
53
+ rejectUnauthorized: true
54
+ } : false
55
+ };
56
+
57
+ const logger = createLogger({ context: 'database' });
58
+ export const dbClient = PostgresFactory.getInstance(config, logger);
59
+ ```
60
+
61
+ ### 2. Inicialização e Queries
62
+
63
+ ```typescript
64
+ // src/server.ts
65
+ import { dbClient } from './database.js';
66
+
67
+ // Inicializar conexão
68
+ await dbClient.initialize();
69
+
70
+ // Query simples
71
+ const users = await dbClient.query<{ id: number; name: string; email: string }>(
72
+ 'SELECT id, name, email FROM users WHERE active = $1',
73
+ [true]
74
+ );
75
+
76
+ console.log(`Found ${users.rowCount} users:`, users.rows);
77
+
78
+ // Cleanup gracioso
79
+ process.on('SIGTERM', async () => {
80
+ await dbClient.close();
81
+ process.exit(0);
82
+ });
83
+ ```
84
+
85
+ ### 3. Transações
86
+
87
+ ```typescript
88
+ // Transação automática (recomendado)
89
+ const result = await dbClient.transaction(async (trx) => {
90
+ // Log de auditoria
91
+ await trx.query('INSERT INTO audit_logs (action, user_id) VALUES ($1, $2)',
92
+ ['USER_CREATED', 123]);
93
+
94
+ // Criar usuário
95
+ const user = await trx.query<{ id: number }>(
96
+ 'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
97
+ ['João Silva', 'joao@example.com']
98
+ );
99
+
100
+ // Criar perfil associado
101
+ await trx.query('INSERT INTO user_profiles (user_id, settings) VALUES ($1, $2)',
102
+ [user.rows[0].id, '{}']);
103
+
104
+ return user.rows[0];
105
+ });
106
+
107
+ console.log('Usuário criado:', result);
108
+
109
+
110
+ // ou manual (não recomendado)
111
+
112
+ const transaction = await client.transaction();
113
+ try {
114
+ await transaction.query('INSERT INTO logs (event) VALUES ($1)', ['MANUAL_TX']);
115
+ await transaction.query('UPDATE users SET active = true WHERE id = $1', [42]);
116
+ await transaction.commit();
117
+ } catch (error) {
118
+ await transaction.rollback();
119
+ }
120
+ ```
121
+
122
+ ## API Reference
123
+
124
+ ### PostgresFactory
125
+
126
+ ```typescript
127
+ interface PostgresConfig {
128
+ // Conexão básica
129
+ host: string;
130
+ port: number;
131
+ database: string;
132
+ user: string;
133
+ password: string;
134
+
135
+ // Pool de conexões
136
+ max?: number; // Máximo de conexões (padrão: 10)
137
+ min?: number; // Mínimo de conexões (padrão: 2)
138
+ connectionTimeoutMillis?: number; // Timeout de conexão (padrão: 60000)
139
+ idleTimeoutMillis?: number; // Timeout de idle (padrão: 30000)
140
+
141
+ // SSL (produção)
142
+ ssl?: boolean | {
143
+ rejectUnauthorized?: boolean;
144
+ ca?: string;
145
+ cert?: string;
146
+ key?: string;
147
+ };
148
+
149
+ // PostgreSQL específico
150
+ statement_timeout?: number;
151
+ query_timeout?: number;
152
+ application_name?: string;
153
+ }
154
+
155
+ // Singleton Factory
156
+ class PostgresDatabaseFactory {
157
+ static getInstance(): PostgresDatabaseFactory;
158
+ getInstance(config: DatabaseConfig, logger?: Logger): DatabaseClient;
159
+ closeInstance(): Promise<void>;
160
+ }
161
+
162
+ // Instância padrão para conveniência
163
+ const PostgresFactory: PostgresDatabaseFactory;
164
+ ```
165
+
166
+ ### PostgresClient
167
+
168
+ ```typescript
169
+ class PostgresClient implements DatabaseClient {
170
+ // Ciclo de vida
171
+ initialize(): Promise<void>;
172
+ close(): Promise<void>;
173
+ isReady(): boolean;
174
+
175
+ // Queries
176
+ query<T>(sql: string, params?: any[]): Promise<QueryResult<T>>;
177
+ transaction<T>(callback: (trx: PostgresTransaction) => Promise<T>): Promise<T>;
178
+
179
+ // Monitoramento
180
+ healthCheck(): Promise<HealthCheckResult>;
181
+ getConnectionInfo(): { total: number; idle: number; waiting: number };
182
+ }
183
+
184
+ interface QueryResult<T> {
185
+ rows: T[];
186
+ rowCount: number;
187
+ fields: Array<{ name: string; dataTypeID: number }>;
188
+ }
189
+
190
+ interface HealthCheckResult {
191
+ status: 'healthy' | 'unhealthy';
192
+ message: string;
193
+ timestamp: Date;
194
+ responseTime: number;
195
+ }
196
+ ```
197
+
198
+ ### PostgresTransaction
199
+
200
+ ```typescript
201
+ class PostgresTransaction extends BaseTransaction {
202
+ query<T>(sql: string, params?: any[]): Promise<QueryResult<T>>;
203
+ commit(): Promise<void>;
204
+ rollback(): Promise<void>;
205
+ isCompleted(): boolean;
206
+ }
207
+
208
+ // Uso manual (não recomendado)
209
+ const trx = await dbClient.transaction();
210
+ try {
211
+ await trx.query('INSERT INTO logs (event) VALUES ($1)', ['START']);
212
+ await trx.query('UPDATE counters SET value = value + 1');
213
+ await trx.commit();
214
+ } catch (error) {
215
+ await trx.rollback();
216
+ throw error;
217
+ }
218
+ ```
219
+
220
+ ## Sistema de Migrações
221
+
222
+ ### PostgresMigrator
223
+
224
+ ```typescript
225
+ import { PostgresMigrator } from '@groundbrick/db-postgres';
226
+
227
+ const migrator = new PostgresMigrator(
228
+ dbClient,
229
+ './migrations',
230
+ logger.child('migrator')
231
+ );
232
+
233
+ // Aplicar todas as migrações pendentes
234
+ await migrator.migrate();
235
+
236
+ // Rollback das últimas N migrações
237
+ await migrator.rollback(2);
238
+
239
+ // Verificar status
240
+ const status = await migrator.getStatus();
241
+ console.log(`Migrações: ${status.applied}/${status.total} aplicadas`);
242
+ console.log('Pendentes:', status.pending);
243
+
244
+ // Listar migrações aplicadas
245
+ const applied = await migrator.getApplied();
246
+ console.log('Aplicadas:', applied.map(m => `${m.id} - ${m.description}`));
247
+ ```
248
+
249
+ ### Estrutura de Arquivos de Migração
250
+
251
+ ```
252
+ migrations/
253
+ ├── 20241201_001_create_users_table.sql
254
+ ├── 20241201_002_add_user_profiles.sql
255
+ ├── 20241202_001_add_indexes.sql
256
+ └── 20241203_001_add_audit_logs.sql
257
+ ```
258
+
259
+ ### Formato de Arquivo de Migração
260
+
261
+ ```sql
262
+ -- Criar tabela de usuários
263
+ CREATE TABLE users (
264
+ id SERIAL PRIMARY KEY,
265
+ name VARCHAR(100) NOT NULL,
266
+ email VARCHAR(100) UNIQUE NOT NULL,
267
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
268
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
269
+ );
270
+
271
+ CREATE INDEX idx_users_email ON users(email);
272
+ CREATE INDEX idx_users_created_at ON users(created_at);
273
+
274
+ -- DOWN
275
+ DROP INDEX IF EXISTS idx_users_created_at;
276
+ DROP INDEX IF EXISTS idx_users_email;
277
+ DROP TABLE IF EXISTS users;
278
+ ```
279
+
280
+ **Regras importantes:**
281
+ - Arquivo deve ter seção `UP` (antes de `-- DOWN`) e seção `DOWN` (após `-- DOWN`)
282
+ - Primeira linha com `--` vira a descrição da migração
283
+ - Arquivos aplicados em ordem lexicográfica
284
+ - Rollback executa a seção `DOWN` em ordem reversa
285
+
286
+ ### Exemplos de Migrações
287
+
288
+ ```sql
289
+ -- migrations/20241201_001_create_users_table.sql
290
+ -- Criar tabela inicial de usuários
291
+ CREATE TABLE users (
292
+ id SERIAL PRIMARY KEY,
293
+ name VARCHAR(100) NOT NULL,
294
+ email VARCHAR(100) UNIQUE NOT NULL,
295
+ password_hash VARCHAR(255) NOT NULL,
296
+ role VARCHAR(20) DEFAULT 'user',
297
+ active BOOLEAN DEFAULT true,
298
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
299
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
300
+ );
301
+
302
+ -- DOWN
303
+ DROP TABLE IF EXISTS users;
304
+ ```
305
+
306
+ ```sql
307
+ -- migrations/20241201_002_add_user_profiles.sql
308
+ -- Adicionar tabela de perfis de usuário
309
+ CREATE TABLE user_profiles (
310
+ id SERIAL PRIMARY KEY,
311
+ user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
312
+ first_name VARCHAR(50),
313
+ last_name VARCHAR(50),
314
+ phone VARCHAR(20),
315
+ avatar_url TEXT,
316
+ settings JSONB DEFAULT '{}',
317
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
318
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
319
+ );
320
+
321
+ CREATE UNIQUE INDEX idx_user_profiles_user_id ON user_profiles(user_id);
322
+
323
+ -- DOWN
324
+ DROP INDEX IF EXISTS idx_user_profiles_user_id;
325
+ DROP TABLE IF EXISTS user_profiles;
326
+ ```
327
+
328
+ ## Exemplos Práticos
329
+
330
+ ### Configuração com Variáveis de Ambiente
331
+
332
+ ```typescript
333
+ // src/config/database.ts
334
+ import { PostgresConfig } from '@groundbrick/db-postgres';
335
+
336
+ export const dbConfig: PostgresConfig = {
337
+ host: process.env.DB_HOST || 'localhost',
338
+ port: parseInt(process.env.DB_PORT || '5432'),
339
+ database: process.env.DB_NAME!,
340
+ user: process.env.DB_USER!,
341
+ password: process.env.DB_PASSWORD!,
342
+
343
+ // Pool otimizado para produção
344
+ max: parseInt(process.env.DB_POOL_MAX || '10'),
345
+ min: parseInt(process.env.DB_POOL_MIN || '2'),
346
+ connectionTimeoutMillis: parseInt(process.env.DB_CONNECTION_TIMEOUT || '60000'),
347
+ idleTimeoutMillis: parseInt(process.env.DB_IDLE_TIMEOUT || '30000'),
348
+
349
+ // SSL em produção
350
+ ssl: process.env.NODE_ENV === 'production' ? {
351
+ rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED === 'true',
352
+ ca: process.env.DB_SSL_CA,
353
+ cert: process.env.DB_SSL_CERT,
354
+ key: process.env.DB_SSL_KEY
355
+ } : false,
356
+
357
+ // Configurações PostgreSQL
358
+ application_name: process.env.APP_NAME || 'microframework-app',
359
+ statement_timeout: 30000,
360
+ query_timeout: 10000
361
+ };
362
+ ```
363
+
364
+ ### Repository Pattern
365
+
366
+ ```typescript
367
+ // src/repositories/UserRepository.ts
368
+ import { DatabaseClient, QueryResult } from '@groundbrick/db-postgres';
369
+
370
+ export interface User {
371
+ id: number;
372
+ name: string;
373
+ email: string;
374
+ active: boolean;
375
+ created_at: Date;
376
+ }
377
+
378
+ export class UserRepository {
379
+ constructor(private db: DatabaseClient) {}
380
+
381
+ async findById(id: number): Promise<User | null> {
382
+ const result = await this.db.query<User>(
383
+ 'SELECT * FROM users WHERE id = $1',
384
+ [id]
385
+ );
386
+ return result.rows[0] || null;
387
+ }
388
+
389
+ async findByEmail(email: string): Promise<User | null> {
390
+ const result = await this.db.query<User>(
391
+ 'SELECT * FROM users WHERE email = $1',
392
+ [email]
393
+ );
394
+ return result.rows[0] || null;
395
+ }
396
+
397
+ async findPaginated(limit: number, offset: number): Promise<{ users: User[]; total: number }> {
398
+ const [usersResult, countResult] = await Promise.all([
399
+ this.db.query<User>(
400
+ 'SELECT * FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2',
401
+ [limit, offset]
402
+ ),
403
+ this.db.query<{ count: string }>(
404
+ 'SELECT COUNT(*) as count FROM users'
405
+ )
406
+ ]);
407
+
408
+ return {
409
+ users: usersResult.rows,
410
+ total: parseInt(countResult.rows[0].count)
411
+ };
412
+ }
413
+
414
+ async create(userData: Omit<User, 'id' | 'created_at'>): Promise<User> {
415
+ const result = await this.db.query<User>(
416
+ `INSERT INTO users (name, email, active)
417
+ VALUES ($1, $2, $3)
418
+ RETURNING *`,
419
+ [userData.name, userData.email, userData.active]
420
+ );
421
+ return result.rows[0];
422
+ }
423
+
424
+ async update(id: number, userData: Partial<Omit<User, 'id' | 'created_at'>>): Promise<User | null> {
425
+ const setClause = Object.keys(userData)
426
+ .map((key, index) => `${key} = $${index + 2}`)
427
+ .join(', ');
428
+
429
+ const values = [id, ...Object.values(userData)];
430
+
431
+ const result = await this.db.query<User>(
432
+ `UPDATE users SET ${setClause}, updated_at = NOW()
433
+ WHERE id = $1
434
+ RETURNING *`,
435
+ values
436
+ );
437
+ return result.rows[0] || null;
438
+ }
439
+
440
+ async delete(id: number): Promise<boolean> {
441
+ const result = await this.db.query(
442
+ 'DELETE FROM users WHERE id = $1',
443
+ [id]
444
+ );
445
+ return (result.rowCount || 0) > 0;
446
+ }
447
+ }
448
+ ```
449
+
450
+ ### Service com Transações
451
+
452
+ ```typescript
453
+ // src/services/UserService.ts
454
+ import { DatabaseClient } from '@groundbrick/db-postgres';
455
+ import { UserRepository } from '../repositories/UserRepository.js';
456
+
457
+ export class UserService {
458
+ constructor(
459
+ private db: DatabaseClient,
460
+ private userRepo: UserRepository
461
+ ) {}
462
+
463
+ async createUserWithProfile(userData: {
464
+ name: string;
465
+ email: string;
466
+ firstName: string;
467
+ lastName: string;
468
+ }): Promise<{ user: User; profile: UserProfile }> {
469
+ return this.db.transaction(async (trx) => {
470
+ // Criar usuário
471
+ const user = await trx.query<User>(
472
+ 'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
473
+ [userData.name, userData.email]
474
+ );
475
+
476
+ // Criar perfil
477
+ const profile = await trx.query<UserProfile>(
478
+ `INSERT INTO user_profiles (user_id, first_name, last_name)
479
+ VALUES ($1, $2, $3) RETURNING *`,
480
+ [user.rows[0].id, userData.firstName, userData.lastName]
481
+ );
482
+
483
+ // Log de auditoria
484
+ await trx.query(
485
+ 'INSERT INTO audit_logs (action, entity_type, entity_id, details) VALUES ($1, $2, $3, $4)',
486
+ ['CREATE', 'user', user.rows[0].id, JSON.stringify(userData)]
487
+ );
488
+
489
+ return {
490
+ user: user.rows[0],
491
+ profile: profile.rows[0]
492
+ };
493
+ });
494
+ }
495
+ }
496
+ ```
497
+
498
+ ### Health Check e Monitoring
499
+
500
+ ```typescript
501
+ // src/health.ts
502
+ import { dbClient } from './database.js';
503
+
504
+ export async function checkDatabaseHealth() {
505
+ const health = await dbClient.healthCheck();
506
+ const connectionInfo = dbClient.getConnectionInfo();
507
+
508
+ return {
509
+ database: {
510
+ status: health.status,
511
+ message: health.message,
512
+ responseTime: health.responseTime,
513
+ timestamp: health.timestamp
514
+ },
515
+ connectionPool: {
516
+ total: connectionInfo.total,
517
+ idle: connectionInfo.idle,
518
+ waiting: connectionInfo.waiting,
519
+ utilization: `${Math.round((connectionInfo.total - connectionInfo.idle) / connectionInfo.total * 100)}%`
520
+ }
521
+ };
522
+ }
523
+
524
+ // src/routes/health.ts (Express)
525
+ app.get('/health', async (req, res) => {
526
+ try {
527
+ const health = await checkDatabaseHealth();
528
+ const statusCode = health.database.status === 'healthy' ? 200 : 503;
529
+ res.status(statusCode).json(health);
530
+ } catch (error) {
531
+ res.status(503).json({
532
+ database: { status: 'unhealthy', message: error.message },
533
+ connectionPool: { total: 0, idle: 0, waiting: 0, utilization: '0%' }
534
+ });
535
+ }
536
+ });
537
+ ```
538
+
539
+ ## Error Handling
540
+
541
+ ### Tipos de Erro Disponíveis
542
+
543
+ ```typescript
544
+ import {
545
+ ConnectionError,
546
+ QueryError,
547
+ TransactionError,
548
+ MigrationError
549
+ } from '@groundbrick/db-postgres';
550
+
551
+ try {
552
+ await dbClient.query('SELECT * FROM users');
553
+ } catch (error) {
554
+ if (error instanceof ConnectionError) {
555
+ console.error('Erro de conexão:', error.message);
556
+ // Tentar reconectar ou usar fallback
557
+ } else if (error instanceof QueryError) {
558
+ console.error('Erro na query:', error.message);
559
+ console.error('SQL:', error.sql);
560
+ console.error('Params:', error.params);
561
+ } else if (error instanceof TransactionError) {
562
+ console.error('Erro na transação:', error.message);
563
+ // Transaction já foi automaticamente revertida
564
+ }
565
+ }
566
+ ```
567
+
568
+ ### Padrão Singleton e Múltiplos Clientes
569
+
570
+ ```typescript
571
+ // Múltiplos clientes para diferentes bases
572
+ const mainDbClient = PostgresFactory.getInstance({
573
+ host: 'localhost',
574
+ database: 'main_app',
575
+ user: 'app_user',
576
+ password: 'secret'
577
+ });
578
+
579
+ const analyticsDbClient = PostgresFactory.getInstance({
580
+ host: 'analytics-server',
581
+ database: 'analytics',
582
+ user: 'analytics_user',
583
+ password: 'secret'
584
+ });
585
+
586
+ // Clients são automaticamente reutilizados se config for idêntica
587
+ const sameClient = PostgresFactory.getInstance({
588
+ host: 'localhost',
589
+ database: 'main_app',
590
+ user: 'app_user',
591
+ password: 'secret'
592
+ }); // Retorna a mesma instância de mainDbClient
593
+
594
+ // Cleanup de todas as conexões
595
+ await PostgresDatabaseFactory.getInstance().closeInstance();
596
+ ```
597
+
598
+ ## Configuração de Ambiente
599
+
600
+ ```bash
601
+ # .env
602
+ # Configuração do Banco
603
+ DB_HOST=localhost
604
+ DB_PORT=5432
605
+ DB_NAME=microframework_app
606
+ DB_USER=postgres
607
+ DB_PASSWORD=your_password
608
+
609
+ # SSL (produção)
610
+ DB_SSL=true
611
+ DB_SSL_REJECT_UNAUTHORIZED=true
612
+ DB_SSL_CA=/path/to/ca.pem
613
+ DB_SSL_CERT=/path/to/cert.pem
614
+ DB_SSL_KEY=/path/to/key.pem
615
+
616
+ # Pool de Conexões
617
+ DB_POOL_MAX=10
618
+ DB_POOL_MIN=2
619
+ DB_CONNECTION_TIMEOUT=60000
620
+ DB_IDLE_TIMEOUT=30000
621
+
622
+ # Configuração da Aplicação
623
+ APP_NAME=microframework-app
624
+ NODE_ENV=production
625
+ LOG_LEVEL=info
626
+ ```
627
+
628
+ ## Migration CLI Script
629
+
630
+ ```typescript
631
+ // scripts/migrate.ts
632
+ import { dbClient } from '../src/database.js';
633
+ import { PostgresMigrator } from '@groundbrick/db-postgres';
634
+ import { createLogger } from '@groundbrick/logger';
635
+
636
+ const logger = createLogger({ context: 'migration-cli' });
637
+ const migrator = new PostgresMigrator(dbClient, './migrations', logger);
638
+
639
+ async function main() {
640
+ const command = process.argv[2];
641
+
642
+ await dbClient.initialize();
643
+
644
+ try {
645
+ switch (command) {
646
+ case 'up':
647
+ await migrator.migrate();
648
+ break;
649
+ case 'down':
650
+ const steps = parseInt(process.argv[3]) || 1;
651
+ await migrator.rollback(steps);
652
+ break;
653
+ case 'status':
654
+ const status = await migrator.getStatus();
655
+ console.log(`Migrations: ${status.applied}/${status.total}`);
656
+ if (status.pending.length > 0) {
657
+ console.log('Pending:', status.pending);
658
+ }
659
+ break;
660
+ default:
661
+ console.log('Usage: npm run migrate [up|down|status] [steps]');
662
+ }
663
+ } finally {
664
+ await dbClient.close();
665
+ }
666
+ }
667
+
668
+ main().catch(console.error);
669
+ ```
670
+
671
+ ```json
672
+ // package.json scripts
673
+ {
674
+ "scripts": {
675
+ "migrate": "tsx scripts/migrate.ts",
676
+ "migrate:up": "npm run migrate up",
677
+ "migrate:down": "npm run migrate down",
678
+ "migrate:status": "npm run migrate status"
679
+ }
680
+ }
681
+ ```
682
+
683
+ ## Integração com Express Adapter
684
+
685
+ ```typescript
686
+ // src/server.ts - Integração com @groundbrick/express-adapter
687
+ import { ExpressApp } from '@groundbrick/express-adapter';
688
+ import { dbClient } from './database.js';
689
+
690
+ const app = new ExpressApp({
691
+ port: 3000,
692
+ cors: { origin: true }
693
+ });
694
+
695
+ // Middleware de database (personalizado)
696
+ app.getApp().use((req, res, next) => {
697
+ req.db = dbClient;
698
+ next();
699
+ });
700
+
701
+ // Health check endpoint
702
+ app.addRoute({
703
+ method: 'get',
704
+ path: '/health',
705
+ handler: async (req, res) => {
706
+ const health = await checkDatabaseHealth();
707
+ const statusCode = health.database.status === 'healthy' ? 200 : 503;
708
+ res.status(statusCode).json(health);
709
+ }
710
+ });
711
+
712
+ // Aplicar migrações na inicialização
713
+ const migrator = new PostgresMigrator(dbClient, './migrations', logger);
714
+ await dbClient.initialize();
715
+ await migrator.migrate();
716
+
717
+ await app.start();
718
+ ```
719
+
720
+ ## Melhores Práticas
721
+
722
+ 1. **Use o padrão Singleton** para evitar múltiplas conexões desnecessárias
723
+ 2. **Sempre inicialize** o cliente antes de usar (`await dbClient.initialize()`)
724
+ 3. **Use transações** para operações que modificam múltiplas tabelas
725
+ 4. **Implemente health checks** para monitoramento de produção
726
+ 5. **Configure SSL** adequadamente em produção
727
+ 6. **Use Repository pattern** para organizar queries
728
+ 7. **Monitore o pool** de conexões em produção
729
+ 8. **Teste migrações** em ambiente de desenvolvimento primeiro
730
+ 9. **Implemente error handling** específico para cada tipo de erro
731
+ 10. **Use TypeScript** para tipagem de queries e configurações
732
+
733
+ ## Dependencies
734
+
735
+ ### Required
736
+ - `@groundbrick/logger` - Sistema de logging
737
+ - `@groundbrick/db-core` - Abstrações de banco de dados
738
+
739
+ ### Peer Dependencies
740
+ - `pg` - Driver PostgreSQL para Node.js (^8.11.0)
741
+
742
+ ### Dev Dependencies
743
+ - `@types/pg` - Tipos TypeScript para pg
744
+
745
+ ## License
746
+
747
+ MIT