Connection_setup
Chapter 5: Connection & DataSource Setup
Section titled “Chapter 5: Connection & DataSource Setup”Deep Dive into TypeORM Connections
Section titled “Deep Dive into TypeORM Connections”5.1 DataSource vs Connection
Section titled “5.1 DataSource vs Connection”In TypeORM, DataSource is the modern way to manage database connections (replacing the deprecated Connection API).
DataSource Architecture ================================================================================
DataSource | +-------------------+-------------------+ | | | | v v v v +-------+ +----------+ +--------+ +-----------+ | Entity| |Repository| | Query | |Migration | |Manager| | Access | |Builder | | Runner | +-------+ +----------+ +--------+ +-----------+ | | | | +-----------+------------+--------------+ | v +-----------+ | Database | +-----------+
================================================================================5.2 DataSource Configuration Options
Section titled “5.2 DataSource Configuration Options”Complete Configuration Reference
Section titled “Complete Configuration Reference”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ // ========================================== // BASIC CONNECTION OPTIONS // ==========================================
type: 'postgres', // Database type host: 'localhost', // Database host port: 5432, // Database port username: 'postgres', // Database username password: 'password', // Database password database: 'my_app', // Database name
// ========================================== // ENTITY CONFIGURATION // ==========================================
// Option 1: Direct entity classes entities: [User, Post, Comment],
// Option 2: Glob patterns (for compiled JS) entities: ['dist/entities/**/*.js'],
// Option 3: Glob patterns (for TypeScript) entities: ['src/entities/**/*.ts'],
// Option 4: Mixed entities: [User, 'dist/entities/**/*.js'],
// ========================================== // MIGRATION CONFIGURATION // ==========================================
migrations: ['dist/migrations/**/*.js'], migrationsTableName: 'migrations', // Table name for migrations migrationsRun: false, // Auto-run on startup
// ========================================== // SUBSCRIBER CONFIGURATION // ==========================================
subscribers: ['dist/subscribers/**/*.js'],
// ========================================== // SCHEMA SYNCHRONIZATION // ==========================================
synchronize: false, // NEVER true in production! dropSchema: false, // Drop schema on close (dev only)
// ========================================== // LOGGING // ==========================================
logging: true, // Enable all logging // OR selective: logging: ['query', 'error', 'migration', 'schema'],
// ========================================== // CONNECTION POOL // ==========================================
poolSize: 10, // Number of connections
// ========================================== // NAMING STRATEGY // ==========================================
namingStrategy: new SnakeNamingStrategy(),
// ========================================== // CACHE // ==========================================
cache: true, // Enable query result caching // OR advanced: cache: { type: 'redis', options: { host: 'localhost', port: 6379, }, duration: 30000, // Cache duration in ms },});5.3 DataSource Lifecycle
Section titled “5.3 DataSource Lifecycle” DataSource Lifecycle +------------------------------------------------------------------+ | | | 1. CREATE | | const dataSource = new DataSource({...}); | | | | +-------------------+ | | | DataSource Created| | | | (Not connected) | | | +-------------------+ | | | | | v | | 2. INITIALIZE | | await dataSource.initialize(); | | | | +-------------------+ | | | DataSource Ready | | | | (Connected to DB) | | | +-------------------+ | | | | | v | | 3. USE | | const users = await dataSource.getRepository(User).find(); | | | | +-------------------+ | | | Execute Queries | | | | Run Migrations | | | +-------------------+ | | | | | v | | 4. DESTROY | | await dataSource.destroy(); | | | | +-------------------+ | | | Connection Closed | | | | Resources Freed | | | +-------------------+ | | | +------------------------------------------------------------------+Lifecycle Implementation
Section titled “Lifecycle Implementation”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres', host: 'localhost', port: 5432, username: 'postgres', password: 'password', database: 'my_app', entities: ['dist/entities/**/*.js'], migrations: ['dist/migrations/**/*.js'], synchronize: false,});
// src/index.tsimport 'reflect-metadata';import { AppDataSource } from './data-source';
async function bootstrap() { try { // Initialize the connection await AppDataSource.initialize(); console.log('DataSource initialized successfully');
// Application logic here...
} catch (error) { console.error('Error during DataSource initialization:', error); process.exit(1); }}
// Graceful shutdownprocess.on('SIGINT', async () => { console.log('Closing application...'); if (AppDataSource.isInitialized) { await AppDataSource.destroy(); console.log('DataSource destroyed'); } process.exit(0);});
bootstrap();5.4 Entity Manager
Section titled “5.4 Entity Manager”The EntityManager is a central service for working with entities.
Entity Manager Responsibilities +------------------------------------------------------------------+ | | | +----------------------------------------------------------+ | | | EntityManager | | | | | | | | +----------------------------------------------------+ | | | | | Repository Access | | | | | | getRepository(User) | | | | | | getCustomRepository(UserRepository) | | | | | +----------------------------------------------------+ | | | | | | | | +----------------------------------------------------+ | | | | | CRUD Operations | | | | | | find(), findOne(), save(), remove() | | | | | +----------------------------------------------------+ | | | | | | | | +----------------------------------------------------+ | | | | | Query Builder | | | | | | createQueryBuilder() | | | | | +----------------------------------------------------+ | | | | | | | | +----------------------------------------------------+ | | | | | Transactions | | | | | | transaction(callback) | | | | | +----------------------------------------------------+ | | | | | | | +----------------------------------------------------------+ | | | +------------------------------------------------------------------+Using Entity Manager
Section titled “Using Entity Manager”import { AppDataSource } from './data-source';import { User } from './entities/user.entity';
async function exampleUsage() { const entityManager = AppDataSource.manager;
// Find all users const users = await entityManager.find(User);
// Find with conditions const activeUsers = await entityManager.find(User, { where: { isActive: true }, order: { createdAt: 'DESC' }, take: 10, });
// Find one const user = await entityManager.findOne(User, { where: { id: 1 }, relations: ['posts'], });
// Save entity const newUser = await entityManager.save(User, { name: 'John Doe', email: 'john@example.com', });
// Remove entity await entityManager.remove(User, { id: 1 });
// Raw query const result = await entityManager.query('SELECT * FROM users WHERE id = $1', [1]);
// Query builder const qb = entityManager.createQueryBuilder(User, 'user'); const usersWithPosts = await qb .leftJoinAndSelect('user.posts', 'post') .where('user.isActive = :active', { active: true }) .getMany();}5.5 Repository Access
Section titled “5.5 Repository Access” Repository Access Methods +------------------------------------------------------------------+ | | | Method 1: From DataSource | | +----------------------------------------------------------+ | | | const userRepo = AppDataSource.getRepository(User); | | | +----------------------------------------------------------+ | | | | Method 2: From Entity Manager | | +----------------------------------------------------------+ | | | const userRepo = entityManager.getRepository(User); | | | +----------------------------------------------------------+ | | | | Method 3: Custom Repository | | +----------------------------------------------------------+ | | | const userRepo = AppDataSource | | | | .getCustomRepository(UserRepository); | | | +----------------------------------------------------------+ | | | | Method 4: Injected in NestJS | | +----------------------------------------------------------+ | | | constructor( | | | | @InjectRepository(User) | | | | private userRepo: Repository<User> | | | | ) {} | | | +----------------------------------------------------------+ | | | +------------------------------------------------------------------+5.6 Connection Options by Database
Section titled “5.6 Connection Options by Database”PostgreSQL
Section titled “PostgreSQL”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres', host: 'localhost', port: 5432, username: 'postgres', password: 'password', database: 'my_app',
// PostgreSQL-specific options schema: 'public', // Default schema ssl: { // SSL configuration rejectUnauthorized: false, }, uuidExtension: 'uuid-ossp', // UUID generation extension
// Native driver options extra: { ssl: true, application_name: 'my_app', statement_timeout: 30000, // 30 second timeout },});import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'my_app',
// MySQL-specific options charset: 'utf8mb4', // Character set timezone: '+00:00', // Server timezone connectTimeout: 10000, // Connection timeout (ms) acquireTimeout: 10000, // Acquire timeout (ms) insecureAuth: false, // Allow old auth
// Connection pool extra: { connectionLimit: 10, waitForConnections: true, queueLimit: 0, },});SQLite
Section titled “SQLite”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'better-sqlite3', // or 'sqlite' database: 'my_app.db', // File path // database: ':memory:', // In-memory database
// SQLite-specific options prepareDatabase: (db: any) => { db.pragma('journal_mode = WAL'); // Enable WAL mode db.pragma('synchronous = NORMAL'); },});MongoDB
Section titled “MongoDB”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'mongodb', url: 'mongodb://localhost:27017', // Connection URL database: 'my_app',
// MongoDB-specific options authSource: 'admin', auth: { username: 'admin', password: 'password', }, useNewUrlParser: true, useUnifiedTopology: true,});5.7 Connection Pooling Deep Dive
Section titled “5.7 Connection Pooling Deep Dive” Connection Pool States +------------------------------------------------------------------+ | | | Pool Configuration | | +----------------------------------------------------------+ | | | | | | | max: 20 - Maximum connections | | | | min: 5 - Minimum idle connections | | | | idle: 30000 - Idle timeout (ms) | | | | acquire: 2000 - Acquire timeout (ms) | | | | | | | +----------------------------------------------------------+ | | | | Pool States | | | | +------------------+ | | | Active (5) | <-- Currently executing queries | | +------------------+ | | | Idle (10) | <-- Available for use | | +------------------+ | | | Waiting (3) | <-- Requests waiting for connection | | +------------------+ | | | | Request Flow: | | | | Request --> Get Connection --> Execute --> Release Connection | | | | | | v v | | [Idle Pool] [Return to Pool] | | | +------------------------------------------------------------------+Pool Configuration Example
Section titled “Pool Configuration Example”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres', // ... basic options
// Pool size (TypeORM option) poolSize: 10,
// Advanced pool options (driver-specific) extra: { // Maximum connections max: 20,
// Minimum idle connections min: 5,
// Idle timeout (ms) idleTimeoutMillis: 30000,
// Connection timeout (ms) connectionTimeoutMillis: 2000,
// Acquire timeout (ms) acquireTimeoutMillis: 5000,
// Connection validation testOnBorrow: true,
// Keep-alive keepAlive: true, keepAliveInitialDelay: 0, },});5.8 Working with Transactions
Section titled “5.8 Working with Transactions” Transaction Flow +------------------------------------------------------------------+ | | | Without Transaction (Dangerous!) | | | | Step 1: Deduct $100 from Account A | | +-------------------+ | | | Balance: $500->$400| | | +-------------------+ | | | | | v | | Step 2: ERROR! System crash | | +-------------------+ | | | Account A: $400 | <-- Lost $100! | | | Account B: $0 | | | +-------------------+ | | | +------------------------------------------------------------------+ | | | With Transaction (Safe!) | | | | BEGIN TRANSACTION | | | | | v | | Step 1: Deduct $100 from Account A | | +-------------------+ | | | Balance: $500->$400| (Not committed yet) | | +-------------------+ | | | | | v | | Step 2: ERROR! | | +-------------------+ | | | ROLLBACK | | | +-------------------+ | | | | | v | | Result: Account A still has $500 | | | +------------------------------------------------------------------+Transaction Methods
Section titled “Transaction Methods”import { AppDataSource } from './data-source';import { User } from './entities/user.entity';import { Account } from './entities/account.entity';
// Method 1: Using transaction callbackasync function transferMoney(fromId: number, toId: number, amount: number) { await AppDataSource.transaction(async (manager) => { // All operations within this callback are in the same transaction
const fromAccount = await manager.findOne(Account, { where: { id: fromId } }); const toAccount = await manager.findOne(Account, { where: { id: toId } });
if (!fromAccount || !toAccount) { throw new Error('Account not found'); }
if (fromAccount.balance < amount) { throw new Error('Insufficient funds'); }
fromAccount.balance -= amount; toAccount.balance += amount;
await manager.save(fromAccount); await manager.save(toAccount);
// If any error is thrown, transaction is rolled back // If callback completes successfully, transaction is committed });}
// Method 2: Manual transaction controlasync function manualTransaction() { const queryRunner = AppDataSource.createQueryRunner();
try { // Start transaction await queryRunner.startTransaction();
// Execute queries const user = await queryRunner.manager.findOne(User, { where: { id: 1 } }); if (user) { user.name = 'Updated Name'; await queryRunner.manager.save(user); }
// Commit transaction await queryRunner.commitTransaction();
} catch (error) { // Rollback on error await queryRunner.rollbackTransaction(); throw error;
} finally { // Release query runner await queryRunner.release(); }}
// Method 3: Isolation levelsasync function transactionWithIsolation() { await AppDataSource.transaction( 'SERIALIZABLE', // Isolation level async (manager) => { // Transaction with SERIALIZABLE isolation const users = await manager.find(User); // ... operations } );}5.9 Multiple DataSources
Section titled “5.9 Multiple DataSources” Multiple DataSource Architecture +------------------------------------------------------------------+ | | | Application | | +----------------------------------------------------------+ | | | | | | | +------------------+ +------------------+ | | | | | Primary | | Replica | | | | | | DataSource | | DataSource | | | | | | (Read/Write) | | (Read Only) | | | | | +------------------+ +------------------+ | | | | | | | | | +---------|---------------------------|-------------------+ | | | | | | v v | | +------------------+ +------------------+ | | | Primary DB | | Replica DB | | | | (Master) | | (Slave) | | | +------------------+ +------------------+ | | | +------------------------------------------------------------------+Multiple DataSource Configuration
Section titled “Multiple DataSource Configuration”import { DataSource } from 'typeorm';
export const PrimaryDataSource = new DataSource({ name: 'primary', type: 'postgres', host: 'primary-db.example.com', port: 5432, username: 'admin', password: 'password', database: 'my_app', entities: ['dist/entities/**/*.js'], migrations: ['dist/migrations/**/*.js'],});
// src/data-sources/replica.data-source.tsimport { DataSource } from 'typeorm';
export const ReplicaDataSource = new DataSource({ name: 'replica', type: 'postgres', host: 'replica-db.example.com', port: 5432, username: 'admin', password: 'password', database: 'my_app', entities: ['dist/entities/**/*.js'], migrations: [], // No migrations on replica});
// src/index.tsimport 'reflect-metadata';import { PrimaryDataSource, ReplicaDataSource } from './data-sources';
async function bootstrap() { // Initialize both connections await Promise.all([ PrimaryDataSource.initialize(), ReplicaDataSource.initialize(), ]);
console.log('Both DataSources initialized');}
// Usageasync function getUser(id: number) { // Read from replica return ReplicaDataSource.getRepository(User).findOne({ where: { id } });}
async function createUser(data: any) { // Write to primary return PrimaryDataSource.getRepository(User).save(data);}5.10 Read/Write Replication
Section titled “5.10 Read/Write Replication”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres',
// Write operations go to master host: 'master-db.example.com', port: 5432, username: 'admin', password: 'password', database: 'my_app',
// Read operations go to replicas replication: { master: { host: 'master-db.example.com', port: 5432, username: 'admin', password: 'password', }, slaves: [ { host: 'replica-1.example.com', port: 5432, username: 'admin', password: 'password', }, { host: 'replica-2.example.com', port: 5432, username: 'admin', password: 'password', }, ], },
entities: ['dist/entities/**/*.js'],});5.11 Connection Events
Section titled “5.11 Connection Events”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres', // ... other options});
// Listen to connection eventsAppDataSource.setOptions({ // ... options});
// After initializationAppDataSource.initialize().then(() => { // Connection established console.log('Connected to database');
// Subscribe to query events AppDataSource.queryRunner?.connection.subscribers.push({ beforeQuery: (event) => { console.log('Before query:', event.query); }, afterQuery: (event) => { console.log('After query:', event.query, 'Time:', event.duration); }, });});5.12 Health Checks
Section titled “5.12 Health Checks”import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({ type: 'postgres', // ... options});
// Health check functionexport async function checkDatabaseHealth(): Promise<{ status: 'healthy' | 'unhealthy'; latency?: number; error?: string;}> { if (!AppDataSource.isInitialized) { return { status: 'unhealthy', error: 'DataSource not initialized' }; }
try { const start = Date.now(); await AppDataSource.query('SELECT 1'); const latency = Date.now() - start;
return { status: 'healthy', latency, }; } catch (error) { return { status: 'unhealthy', error: error instanceof Error ? error.message : 'Unknown error', }; }}
// Express health endpointapp.get('/health', async (req, res) => { const dbHealth = await checkDatabaseHealth();
res.json({ status: dbHealth.status, database: dbHealth, timestamp: new Date().toISOString(), });});5.13 Error Handling
Section titled “5.13 Error Handling”import { DataSource, QueryFailedError } from 'typeorm';import { AppDataSource } from './data-source';
async function handleDatabaseOperation() { try { const user = await AppDataSource.getRepository(User).findOne({ where: { email: 'test@example.com' }, }); return user;
} catch (error) { if (error instanceof QueryFailedError) { // Database-specific errors
const driverError = error.driverError;
// Unique constraint violation if (driverError.code === '23505') { throw new Error('Email already exists'); }
// Foreign key violation if (driverError.code === '23503') { throw new Error('Referenced record not found'); }
// Not null violation if (driverError.code === '23502') { throw new Error('Required field is missing'); }
// Log the error console.error('Query failed:', error.message); throw new Error('Database operation failed'); }
// Re-throw other errors throw error; }}5.14 Graceful Shutdown
Section titled “5.14 Graceful Shutdown”import 'reflect-metadata';import { AppDataSource } from './data-source';
async function bootstrap() { try { await AppDataSource.initialize(); console.log('Database connected');
// Start your application...
} catch (error) { console.error('Failed to connect to database:', error); process.exit(1); }}
// Graceful shutdown handlersconst gracefulShutdown = async (signal: string) => { console.log(`\n${signal} received. Shutting down gracefully...`);
try { if (AppDataSource.isInitialized) { await AppDataSource.destroy(); console.log('Database connection closed'); } process.exit(0); } catch (error) { console.error('Error during shutdown:', error); process.exit(1); }};
// Register shutdown handlersprocess.on('SIGINT', () => gracefulShutdown('SIGINT'));process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); gracefulShutdown('uncaughtException');});process.on('unhandledRejection', (reason) => { console.error('Unhandled Rejection:', reason); gracefulShutdown('unhandledRejection');});
bootstrap();5.15 Summary
Section titled “5.15 Summary” DataSource Key Points +------------------------------------------------------------------+ | | | Concept | Description | | ---------------------|---------------------------------------- | | DataSource | Main connection configuration object | | initialize() | Establish database connection | | destroy() | Close database connection | | manager | Entity Manager for operations | | getRepository() | Get repository for entity | | transaction() | Run operations in transaction | | query() | Execute raw SQL queries | | isInitialized | Check if connection is active | | | +------------------------------------------------------------------+Next Chapter
Section titled “Next Chapter”Chapter 6: Entity Definition & Decorators
Last Updated: February 2026