Skip to content

DataSources

A DataSource manages database connections and supports schema auto-discovery from repositories.

PostgreSQL First

IGNIS currently focuses on PostgreSQL as the primary database. Support for other database systems (MySQL, SQLite, etc.) is planned for future releases.

Creating a DataSource

typescript
// src/datasources/postgres.datasource.ts
import {
  BaseDataSource,
  datasource,
  ValueOrPromise,
} from '@venizia/ignis';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';

interface IDSConfigs {
  host: string;
  port: number;
  database: string;
  user: string;
  password: string;
}

@datasource({ driver: 'node-postgres' })
export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
  constructor() {
    super({
      name: PostgresDataSource.name,
      config: {
        host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
        port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
        database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
        user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
        password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
      },
      // No schema needed - auto-discovered from @repository bindings!
    });
  }

  override configure(): ValueOrPromise<void> {
    // getSchema() auto-discovers models from @repository bindings
    const schema = this.getSchema();

    this.logger.debug(
      '[configure] Auto-discovered schema | Keys: %o',
      Object.keys(schema),
    );

    this.pool = new Pool(this.settings);
    this.connector = drizzle({ client: this.pool, schema });
  }

  override getConnectionString(): ValueOrPromise<string> {
    const { host, port, user, password, database } = this.settings;
    return `postgresql://${user}:${password}@${host}:${port}/${database}`;
  }
}

How auto-discovery works:

  1. @repository decorators register model-datasource bindings in the MetadataRegistry
  2. When configure() is called, getSchema() invokes discoverSchema() which calls MetadataRegistry.buildSchema({ dataSource }) to collect all bound models and their relations
  3. Drizzle is initialized with the complete schema (tables + Drizzle relations)

You can disable auto-discovery per datasource via @datasource({ driver: 'node-postgres', autoDiscovery: false }).

Manual Schema (Optional)

If you need explicit control, you can still provide schema manually:

typescript
@datasource({ driver: 'node-postgres' })
export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
  constructor() {
    super({
      name: PostgresDataSource.name,
      config: { /* ... */ },
      schema: {
        User: User.schema,
        Configuration: Configuration.schema,
        // Add relations if using Drizzle's relational queries
      },
    });
  }
}

DataSource Hierarchy

AbstractDataSource extends BaseHelper
  └── BaseDataSource
        ├── configure()               # Setup pool + Drizzle connector (abstract)
        ├── getConnectionString()     # Build connection URL (abstract)
        ├── getSchema()               # Auto-discover from @repository bindings
        ├── discoverSchema()          # Internal: reads MetadataRegistry
        ├── hasDiscoverableModels()   # Check if any repos reference this DS
        ├── beginTransaction(opts?)   # Start transaction with isolation level
        ├── getConnector()            # Get Drizzle connector
        └── getSettings()            # Get connection config

Registering a DataSource

typescript
// src/application.ts
export class Application extends BaseApplication {
  preConfigure(): ValueOrPromise<void> {
    this.dataSource(PostgresDataSource);
  }
}

DataSources are bound as singletons to ensure connection pool sharing across the application.

Supported Drivers

DriverPackageStatus
node-postgrespgSupported
mysql2mysql2Planned
better-sqlite3better-sqlite3Planned

DataSource Template

typescript
import { BaseDataSource, datasource, ValueOrPromise } from '@venizia/ignis';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';

interface IDSConfigs {
  host: string;
  port: number;
  database: string;
  user: string;
  password: string;
}

@datasource({ driver: 'node-postgres' })
export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
  constructor() {
    super({
      name: PostgresDataSource.name,
      config: {
        host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
        port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
        database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
        user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
        password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
      },
    });
  }

  override configure(): ValueOrPromise<void> {
    const schema = this.getSchema();
    this.pool = new Pool(this.settings);
    this.connector = drizzle({ client: this.pool, schema });
  }

  override getConnectionString(): ValueOrPromise<string> {
    const { host, port, user, password, database } = this.settings;
    return `postgresql://${user}:${password}@${host}:${port}/${database}`;
  }
}

Deep Dive: See BaseDataSource Reference for connection pooling and advanced configuration.

See Also