JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 28
  • Score
    100M100P100Q50024F
  • License MIT

Core database interfaces and types for the TypeScript microframework

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-core

You will also need to install a specific adapter package such as @200systems/db-postgres or @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 to DatabaseClient)

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:

  • ConnectionError
  • QueryError
  • MigrationError
  • TransactionError
  • DatabaseError (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