FutoIn Database Interface

1.9.12 · active · verified Wed Apr 22

futoin-database is a neutral database interface designed for microservices, providing advanced Query and a unique 'revolutionary' Transaction builder. As a reference implementation for FTN17: FutoIn Interface - Database, it focuses on enforcing specific patterns by design, notably lacking direct support for large result sets, cursors, and explicit client-side transaction control to prevent common database anti-patterns. The library leverages a 'single call' transaction pattern, executing multiple statements on the DB side within a single transaction, with back references for sequential query data. It supports auto-configuration through environment variables for connection pooling and works with MySQL, PostgreSQL, and SQLite out of the box. The current stable version is 1.9.12, and it appears to be actively maintained, often with minor version updates. Its core differentiators include its opinionated approach to database interaction and its tight integration with the FutoIn ecosystem, requiring `futoin-asyncsteps`, `futoin-executor`, and `futoin-invoker` as peer dependencies.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates auto-configuration of an in-memory SQLite database, executing a basic query (Level 1), and a multi-statement transaction with an implicit rollback on failure (Level 2). It showcases the FutoIn database interface's core usage patterns, including integration with `futoin-asyncsteps` and `futoin-executor`.

/*
  Installation:
  npm install futoin-asyncsteps futoin-executor futoin-invoker futoin-database sqlite3
  (sqlite3 is an optional peer dependency for SQLite support that must be explicitly installed if you use it.)
*/

// Set environment variables for a test in-memory SQLite database.
// In a real application, these would be set externally (e.g., .env file, Docker config).
process.env.DB_TYPE = 'sqlite';
process.env.DB_PATH = ':memory:'; // Use an in-memory SQLite DB for quick demonstration
process.env.DB_MAXCONN = '1';
process.env.FUTOIN_DB_ALIAS = 'my_sqlite_db'; // Custom alias for clarity

const AsyncSteps = require('futoin-asyncsteps');
const Executor = require('futoin-executor'); // Required for ContainerConnectionManager
const { AutoConfig } = require('futoin-database');

// Create a FutoIn Container Connection Manager (CCM)
const ccm = new Executor.ContainerConnectionManager();

// Main execution block using AsyncSteps for flow control
AsyncSteps((as) => {
    // 1. Auto-configure the database connection using environment variables.
    // The configuration object indicates supported types, and the last argument
    // is the alias under which the DB service will be registered in CCM.
    AutoConfig(as, ccm, {
        [process.env.FUTOIN_DB_ALIAS]: { type: ['sqlite'] }
    }, process.env.FUTOIN_DB_ALIAS);

    as.add((as) => {
        console.log(`Database service '${process.env.FUTOIN_DB_ALIAS}' configured.`);
        // 2. Get the database service instance from the CCM
        const db = ccm.get(process.env.FUTOIN_DB_ALIAS);

        // 3. Execute a simple query (Level 1 interface)
        console.log('\n--- Executing simple query (Level 1) ---');
        db.query(as, 'SELECT ? + ? AS result_value', [10, 20]);

        as.add((as, rows) => {
            console.log('Query result:', rows);
            console.log('10 + 20 =', rows[0].result_value);

            // 4. Execute a multi-statement transaction (Level 2 interface)
            // This transaction creates a table, inserts data, and then selects it.
            // If any step fails, the entire transaction is rolled back.
            console.log('\n--- Executing transaction (Level 2) ---');
            const tx = [
                // Statement 0: Create table if not exists
                'CREATE TABLE IF NOT EXISTS sample_users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT)',
                // Statement 1: Insert data for User A
                { query: 'INSERT INTO sample_users (name, email) VALUES (?, ?)', params: ['Charlie', 'charlie@example.com'] },
                // Statement 2: Insert data for User B
                { query: 'INSERT INTO sample_users (name, email) VALUES (?, ?)', params: ['Diana', 'diana@example.com'] },
                // Statement 3: Select data for User A by name
                { query: 'SELECT id, name, email FROM sample_users WHERE name = ?', params: ['Charlie'], expect: 'rows' },
                // Statement 4: Select data for User B by email
                { query: 'SELECT id, name, email FROM sample_users WHERE email = ?', params: ['diana@example.com'], expect: 'rows' }
            ];

            db.transaction(as, tx);
            as.add((as, results) => {
                console.log('Transaction results:');
                // 'results' is an array where each element corresponds to the outcome of a statement in 'tx'.
                // For 'expect: rows', it will contain an array of row objects.
                console.log('Charlie data:', results[3][0]); // Result of statement 3
                console.log('Diana data:', results[4][0]);   // Result of statement 4

                if (results[3][0].name === 'Charlie' && results[4][0].name === 'Diana') {
                    console.log('Transaction successful: data inserted and retrieved correctly.');
                }
            });
        });
    });
},(as, err) => {
    console.error('An error occurred during execution:', err.message);
    // Ensure connections are released on error
    ccm.release();
    process.exit(1);
}).whenEnd(() => {
    console.log('\nQuickstart execution complete. Releasing resources.');
    // Ensure connections are released upon successful completion
    ccm.release();
    process.exit(0);
});

view raw JSON →