Package Exports
- @200systems/mf-db-core
Readme
@200systems/mf-db-core
Database abstraction core for the BitBrick microframework
This package provides the backbone for integrating with relational databases (PostgreSQL or MySQL), offering a unified layer for connection handling, querying, transactions, and migrations.
๐ง Key Features
- Unified interface for database clients
- Adapter support for PostgreSQL and MySQL (via external factories)
- Full support for transactions
- Strongly-typed migration system
- Configuration validation and health checks
- Granular error handling by operation type
๐ฆ Installation
npm install @200systems/mf-db-coreYou will also need to install a specific adapter package such as
@200systems/db-postgresor@200systems/db-mysql.
๐ Quick Start
import { DatabaseAdapter } from '@200systems/mf-db-core';
const adapter = new DatabaseAdapter({
type: 'postgresql',
host: 'localhost',
user: 'admin',
password: 'secret',
database: 'mydb',
port: 5432
});
await adapter.initialize();
const users = await adapter.query('SELECT * FROM users WHERE active = $1', [true]);
await adapter.close();๐ Transactions
await adapter.transaction(async (trx) => {
await trx.query('INSERT INTO logs (event, ts) VALUES ($1, now())', ['create_user']);
const result = await trx.query<{ id: number }>(
'INSERT INTO users (email, active) VALUES ($1, $2) RETURNING id',
['user@example.com', true]
);
const userId = result.rows[0].id;
await trx.query('INSERT INTO profiles (user_id) VALUES ($1)', [userId]);
});- The transaction will automatically commit if successful
- Any thrown error will rollback the transaction
- Nested transactions are not natively supported
๐งช Health Check
const status = await adapter.healthCheck();
if (status.status !== 'ok') {
throw new Error(`Database unavailable: ${status.reason}`);
}๐ Pool Info (if supported by the adapter)
const pool = adapter.getClient().getConnectionInfo?.();
if (pool) {
console.log(`Pool: total=${pool.total}, idle=${pool.idle}, waiting=${pool.waiting}`);
}โฑ Readiness Check
if (!adapter.isReady()) {
console.warn('Adapter is not ready yet.');
}๐ฆ Architecture Overview
๐ DatabaseAdapter
This class encapsulates adapter selection (PostgreSQL or MySQL), dynamically initializes the correct client, and exposes high-level methods:
.initialize()/.close().query(sql, params).transaction(cb).healthCheck().getClient()(direct access toDatabaseClient)
The dynamic loading logic (e.g.,
PostgresFactory.getInstance) must be implemented in the actual adapter packages.
๐ง DatabaseClient Interface
Contract that all database adapters must implement:
interface DatabaseClient {
initialize(): Promise<void>;
close(): Promise<void>;
query<T>(sql: string, params?: any[]): Promise<QueryResult<T>>;
transaction<T>(cb: (trx: DatabaseTransaction) => Promise<T>): Promise<T>;
healthCheck(): Promise<HealthCheckResult>;
isReady(): boolean;
}๐ DatabaseTransaction
Within a transaction, the callback receives a DatabaseTransaction instance, allowing you to perform isolated queries.
๐ Project Structure
mf-db-core/
โโโ src/
โ โโโ adapters/ # Generic adapter layer
โ โโโ base/ # Reusable base logic (e.g., transactions)
โ โโโ custom-types/ # Utility and internal types
โ โโโ error-handling/ # Operation-specific error classes
โ โโโ interfaces/ # Public contract definitions
โ โโโ index.ts # Entry point๐งฌ Migrations
The core defines types for managing and recording schema migrations. You can implement your own MigrationManager:
const manager: MigrationManager = getCustomMigrationManager();
const pending = await manager.getPendingMigrations();
for (const migration of pending) {
try {
await manager.applyMigration(migration);
console.log(`Migration ${migration.version} applied`);
} catch (err) {
console.error(`Failed to apply migration ${migration.version}:`, err);
break;
}
}Each migration should follow the Migration type contract with up, down, and version fields.
๐ฃ Error Handling
All operations raise specific error classes for easier diagnosis:
ConnectionErrorQueryErrorMigrationErrorTransactionErrorDatabaseError(generic base class)
๐งฉ Creating a Custom Adapter
Example: PostgreSQL Adapter Factory
export class PostgresFactory {
static getInstance(config: DatabaseConfig, logger: Logger): DatabaseClient {
return new PostgresClient(config, logger);
}
}Inside DatabaseAdapter, you would dynamically import and use this factory depending on the type.
๐ License
MIT โ ยฉ 200Systems