@takepack/db-ext
v2.0.0
Published
A NestJS library providing dynamic database configuration with TypeORM, migration management, and seeding capabilities
Maintainers
Readme
@takepack/db-ext
A comprehensive NestJS library providing dynamic database configuration with TypeORM, migration management, seeding capabilities, and CLI commands.
Features
- 🚀 Dynamic Database Configuration - Easy setup with static or async configuration
- 🔄 Migration Management - Run, revert, and generate migrations with auto-run support
- 🌱 Database Seeding - Flexible seeding system with transaction support
- 🛠️ CLI Commands - Built-in commands for migrations and seeders
- 📦 Factory Pattern - Base factory class for generating test data
- 🎯 TypeORM Integration - Seamless integration with TypeORM
- 🔌 Modular Design - Export command runners for use in other CLI modules
Installation
npm install @takepack/db-extPeer Dependencies
npm install @nestjs/common @nestjs/core @nestjs/typeorm typeorm reflect-metadata rxjs nest-commanderDatabase Drivers
Install the appropriate database driver:
# PostgreSQL
npm install pg
# MySQL/MariaDB
npm install mysql2
# SQLite
npm install sqlite3
# MSSQL
npm install mssqlOptional Dependencies
# For factories and seeders
npm install @faker-js/faker
# For TypeScript migrations
npm install ts-nodeQuick Start
1. Basic Configuration
// app.module.ts
import { Module } from '@nestjs/common';
import { DatabaseExtensionModule } from '@takepack/db-ext';
import { User } from './entities/user.entity';
@Module({
imports: [
DatabaseExtensionModule.register({
connection: {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'user',
password: 'password',
database: 'mydb',
synchronize: false,
logging: true,
},
entities: [User],
migration: {
enabled: true,
migrationsPath: 'src/database/migrations',
tableName: 'migrations',
autoRun: false,
},
seeder: {
enabled: true,
seeders: [],
autoRun: false,
},
}),
],
})
export class AppModule {}2. Async Configuration
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DatabaseExtensionModule } from '@takepack/db-ext';
@Module({
imports: [
ConfigModule.forRoot(),
DatabaseExtensionModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
connection: {
type: 'postgres',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_DATABASE'),
synchronize: false,
logging: configService.get('NODE_ENV') !== 'production',
},
entities: [__dirname + '/**/*.entity{.ts,.js}'],
migration: {
enabled: true,
migrationsPath: 'src/database/migrations',
autoRun: configService.get('NODE_ENV') === 'production',
},
seeder: {
enabled: true,
seeders: [],
autoRun: configService.get('NODE_ENV') === 'development',
},
}),
}),
],
})
export class AppModule {}Migrations
Creating a Migration
Create a migration file manually:
// src/database/migrations/1234567890-CreateUsersTable.ts
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
export class CreateUsersTable1234567890 implements MigrationInterface {
name = 'CreateUsersTable1234567890';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'uuid',
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'email',
type: 'varchar',
isUnique: true,
},
{
name: 'name',
type: 'varchar',
},
{
name: 'created_at',
type: 'timestamp',
default: 'now()',
},
],
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('users');
}
}Using Migration Service
import { Injectable } from '@nestjs/common';
import { MigrationService } from '@takepack/db-ext';
@Injectable()
export class MyService {
constructor(private readonly migrationService: MigrationService) {}
async runMigrations() {
await this.migrationService.runMigrations();
}
async revertMigration() {
await this.migrationService.revertMigration();
}
async checkStatus() {
const hasPending = await this.migrationService.hasPendingMigrations();
console.log('Has pending migrations:', hasPending);
}
}Seeders
Creating a Seeder
// src/database/seeders/user.seeder.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseSeeder } from '@takepack/db-ext';
import { User } from '../entities/user.entity';
@Injectable()
export class UserSeeder extends BaseSeeder {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {
super();
}
async seed(): Promise<void> {
const users = [
{ email: '[email protected]', name: 'Admin User' },
{ email: '[email protected]', name: 'Regular User' },
];
for (const userData of users) {
const exists = await this.userRepository.findOne({
where: { email: userData.email },
});
if (!exists) {
await this.userRepository.save(userData);
console.log(`✓ Created user: ${userData.email}`);
}
}
}
}Using Factory Pattern
// src/database/factories/user.factory.ts
import { Injectable } from '@nestjs/common';
import { BaseFactory } from '@takepack/db-ext';
import { User } from '../entities/user.entity';
import { faker } from '@faker-js/faker';
@Injectable()
export class UserFactory extends BaseFactory<User> {
protected definition(): Partial<User> {
return {
email: faker.internet.email(),
name: faker.person.fullName(),
createdAt: faker.date.past(),
};
}
}// src/database/seeders/user.seeder.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseSeeder } from '@takepack/db-ext';
import { User } from '../entities/user.entity';
import { UserFactory } from '../factories/user.factory';
@Injectable()
export class UserSeeder extends BaseSeeder {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly userFactory: UserFactory,
) {
super();
}
async seed(): Promise<void> {
// Create 50 random users using factory
const users = await this.userFactory.createMany(50);
await this.userRepository.save(users);
console.log('✓ Created 50 users');
}
}Register Seeders
// app.module.ts
DatabaseExtensionModule.register({
// ... other config
seeder: {
enabled: true,
seeders: [UserSeeder, ProductSeeder, CategorySeeder],
autoRun: false,
},
}),Using Seeder Service
import { Injectable } from '@nestjs/common';
import { SeederService } from '@takepack/db-ext';
@Injectable()
export class MyService {
constructor(private readonly seederService: SeederService) {}
async seedDatabase() {
// Run all seeders
await this.seederService.runSeeders();
// Run specific seeders
await this.seederService.runSeeders({
only: ['UserSeeder', 'ProductSeeder'],
});
// Run with custom transaction mode
await this.seederService.runSeeders({
transaction: 'each', // 'all' | 'each' | 'none'
});
}
}CLI Commands
Setting Up CLI
Create a CLI module to use the exported command runners:
// cli.module.ts
import { Module } from '@nestjs/common';
import { MigrationCommandRunner, SeederCommandRunner } from '@takepack/db-ext';
import { AppModule } from './app.module';
@Module({
imports: [AppModule],
providers: [MigrationCommandRunner, SeederCommandRunner],
})
export class CliModule {}// cli.ts
import { CommandFactory } from 'nest-commander';
import { CliModule } from './cli.module';
async function bootstrap() {
await CommandFactory.run(CliModule);
}
bootstrap();Update package.json:
{
"scripts": {
"cli": "ts-node src/cli.ts"
}
}Migration Commands
# Run all pending migrations
npm run cli migration run
# Revert last migration
npm run cli migration revert
# Generate new migration
npm run cli migration generate CreateUsersTable
# Show migration status
npm run cli migration status
# Show pending migrations
npm run cli migration pending
# Show executed migrations
npm run cli migration executedSeeder Commands
# Run all seeders
npm run cli seed
# Run specific seeders
npm run cli seed UserSeeder ProductSeeder
# Run with options
npm run cli seed --only UserSeeder,ProductSeeder
npm run cli seed --transaction each
# List available seeders
npm run cli seed --listConfiguration Options
DatabaseModuleConfig
interface DatabaseModuleConfig {
connection: DatabaseConnectionConfig;
entities: any[];
migration?: MigrationConfig;
seeder?: SeederConfig;
}DatabaseConnectionConfig
interface DatabaseConnectionConfig {
type: 'postgres' | 'mysql' | 'mariadb' | 'sqlite' | 'mssql';
host: string;
port: number;
username: string;
password: string;
database: string;
synchronize?: boolean; // Default: false
logging?: boolean; // Default: false
}MigrationConfig
interface MigrationConfig {
enabled: boolean;
migrationsPath: string;
tableName?: string; // Default: 'migrations'
autoRun?: boolean; // Default: false
}SeederConfig
interface SeederConfig {
enabled: boolean;
seeders: any[];
autoRun?: boolean; // Default: false
}Exported Components
The library exports the following components for use in your application:
Modules
DatabaseExtensionModule- Main module
Services
MigrationService- Migration management serviceSeederService- Seeder management service
Commands (for CLI integration)
MigrationCommandRunner- Migration CLI commandsSeederCommandRunner- Seeder CLI commands
Helpers
BaseSeeder- Base class for seedersBaseFactory- Base class for factoriesConfigBuilder- Helper for building configurations
Types
DatabaseModuleConfigDatabaseConnectionConfigMigrationConfigSeederConfigSeederInterface
Best Practices
1. Use Async Configuration for Production
Always use environment variables and async configuration in production:
DatabaseExtensionModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
connection: {
type: config.get('DB_TYPE'),
host: config.get('DB_HOST'),
// ...
},
}),
});2. Disable Auto-Run in Production
migration: {
enabled: true,
autoRun: process.env.NODE_ENV !== 'production',
}3. Use Transactions for Seeders
await seederService.runSeeders({
transaction: 'all', // Run all seeders in one transaction
});4. Order Your Seeders
seeder: {
enabled: true,
seeders: [
CategorySeeder, // Run first (no dependencies)
ProductSeeder, // Run second (depends on categories)
UserSeeder, // Run third
],
}5. Use Factories for Test Data
// In tests
const users = await userFactory.createMany(100);
await userRepository.save(users);Examples
Complete Application Setup
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DatabaseExtensionModule } from '@takepack/db-ext';
import { User } from './entities/user.entity';
import { Product } from './entities/product.entity';
import { UserSeeder } from './database/seeders/user.seeder';
import { ProductSeeder } from './database/seeders/product.seeder';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
DatabaseExtensionModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
connection: {
type: 'postgres',
host: config.get('DB_HOST', 'localhost'),
port: config.get('DB_PORT', 5432),
username: config.get('DB_USERNAME', 'postgres'),
password: config.get('DB_PASSWORD', 'password'),
database: config.get('DB_DATABASE', 'mydb'),
synchronize: false,
logging: config.get('NODE_ENV') !== 'production',
},
entities: [User, Product],
migration: {
enabled: true,
migrationsPath: 'src/database/migrations',
tableName: 'migrations',
autoRun: config.get('AUTO_RUN_MIGRATIONS') === 'true',
},
seeder: {
enabled: true,
seeders: [UserSeeder, ProductSeeder],
autoRun: config.get('AUTO_RUN_SEEDERS') === 'true',
},
}),
}),
],
})
export class AppModule {}CLI Setup with Commands
// cli.module.ts
import { Module } from '@nestjs/common';
import { MigrationCommandRunner, SeederCommandRunner } from '@takepack/db-ext';
import { AppModule } from './app.module';
@Module({
imports: [AppModule],
providers: [MigrationCommandRunner, SeederCommandRunner],
})
export class CliModule {}
// cli.ts
import { CommandFactory } from 'nest-commander';
import { CliModule } from './cli.module';
async function bootstrap() {
await CommandFactory.run(CliModule, {
errorHandler: (err) => {
console.error('CLI Error:', err);
process.exit(1);
},
});
}
bootstrap();Troubleshooting
Migrations Not Running
Check that migrations are enabled:
migration: { enabled: true }Verify migrations path exists and contains migration files
Ensure migration files follow naming convention:
timestamp-Name.ts
Seeders Not Found
Verify seeders are registered in the module:
seeder: { seeders: [UserSeeder] }Check that seeder classes extend
BaseSeederand have@Injectable()decoratorMake sure seeder dependencies are properly injected
Command Not Found
Install
nest-commander:npm install nest-commanderVerify CLI module imports the main application module
Check that command runners are provided in the CLI module
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues and questions, please open an issue on the GitHub repository.
