JSPM

nanodb-orm

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

Generic database package with Drizzle ORM, auto-migrations, and schema introspection

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (nanodb-orm) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    nanodb-orm

    A production-ready, generic database package built on top of Drizzle ORM with automatic migrations, schema introspection, atomic transactions, and support for both local SQLite and remote Turso databases.

    Features

    • ๐Ÿš€ Auto-Migrations: Automatically creates and updates database schemas from Drizzle table definitions
    • ๐Ÿ” Schema Introspection: Comprehensive schema analysis and validation
    • ๐ŸŒ Multi-Database Support: Works with local SQLite and remote Turso databases
    • โšก Atomic Transactions: Full transaction support with rollback protection
    • ๐Ÿ›ก๏ธ Type Safe: Full TypeScript support with proper type inference
    • ๐Ÿ”’ Security: SQL injection protection and input validation
    • ๐Ÿงต Thread Safe: Race condition fixes and concurrent access protection
    • ๐Ÿ“ฆ NPM Package Ready: Designed to be used as a standalone npm package
    • โš™๏ธ Configurable: Flexible migration and seeding options
    • ๐Ÿงช Test Ready: Built-in testing utilities and isolation
    • ๐Ÿ”ง Production Ready: Enhanced error handling and reliability features

    Installation

    npm install nanodb-orm

    Quick Start

    1. Define Your Models with Drizzle

    Create your Drizzle table definitions (e.g., models/users.ts):

    // models/users.ts
    import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
    
    export const usersTable = sqliteTable('users', {
      id: integer('id').primaryKey({ autoIncrement: true }),
      name: text('name').notNull(),
      age: integer('age').notNull(),
      email: text('email').unique().notNull(),
    });
    
    export type InsertUser = typeof usersTable.$inferInsert;
    export type SelectUser = typeof usersTable.$inferSelect;
    // models/posts.ts
    import { sql } from 'drizzle-orm';
    import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
    import { usersTable } from './users';
    
    export const postsTable = sqliteTable('posts', {
      id: integer('id').primaryKey({ autoIncrement: true }),
      title: text('title').notNull(),
      content: text('content').notNull(),
      userId: integer('user_id')
        .notNull()
        .references(() => usersTable.id, { onDelete: 'cascade' }),
      createdAt: text('created_at')
        .default(sql`(datetime('now'))`)
        .notNull(),
      updatedAt: text('updated_at').$onUpdate(() => new Date().toISOString()),
    });
    
    export type InsertPost = typeof postsTable.$inferInsert;
    export type SelectPost = typeof postsTable.$inferSelect;
    // models/categories.ts
    import { sql } from 'drizzle-orm';
    import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
    
    export const categoriesTable = sqliteTable('categories', {
      id: integer('id').primaryKey({ autoIncrement: true }),
      name: text('name').notNull(),
      description: text('description'),
      color: text('color').notNull().default('#000000'),
      isActive: integer('is_active').notNull().default(1),
      createdAt: text('created_at')
        .default(sql`(datetime('now'))`)
        .notNull(),
    });
    
    export type InsertCategory = typeof categoriesTable.$inferInsert;
    export type SelectCategory = typeof categoriesTable.$inferSelect;
    // models/index.ts
    export * from './users';
    export * from './posts';
    export * from './categories';
    
    // Import tables for aggregation
    import { usersTable } from './users';
    import { postsTable } from './posts';
    import { categoriesTable } from './categories';
    
    // Export aggregated tables for nanodb-orm
    export const tables = {
      users: usersTable,
      posts: postsTable,
      categories: categoriesTable,
    } as const;

    2. Initialize and Setup Database

    import { initializeDatabase, DatabaseSync } from 'nanodb-orm';
    import { tables } from './models';
    
    // Initialize the database package with your Drizzle tables
    initializeDatabase({
      tables,
      seedData: {
        users: [
          { name: 'John Doe', age: 30, email: 'john@example.com' },
          { name: 'Jane Smith', age: 25, email: 'jane@example.com' }
        ],
        posts: [
          { title: 'Welcome Post', content: 'This is my first post!', userId: 1 },
          { title: 'Getting Started', content: 'Here are some tips...', userId: 2 }
        ],
        categories: [
          { name: 'Technology', description: 'Tech-related posts', color: '#3B82F6' },
          { name: 'Lifestyle', description: 'Life and personal posts', color: '#10B981' }
        ]
      }
    });
    
    // Setup database (creates tables, runs migrations, seeds data)
    await DatabaseSync.setup();

    3. Working with Drizzle Tables

    nanodb-orm works seamlessly with Drizzle ORM table definitions. Here are the key Drizzle column types and methods:

    Drizzle Column Types

    • integer() - Integer numbers
    • text() - Text strings
    • real() - Floating point numbers
    • blob() - Binary data

    Drizzle Column Methods

    • .primaryKey({ autoIncrement: true }) - Primary key with auto-increment
    • .notNull() - NOT NULL constraint
    • .unique() - UNIQUE constraint
    • .default(value) - Default value
    • .references(table.column) - Foreign key reference

    Example Drizzle Column Definitions

    // Primary key with auto-increment
    id: integer('id').primaryKey({ autoIncrement: true })
    
    // Required text field
    name: text('name').notNull()
    
    // Optional text field with default
    color: text('color').notNull().default('#000000')
    
    // Unique email field
    email: text('email').unique().notNull()
    
    // Boolean field (stored as integer)
    isActive: integer('is_active').notNull().default(1)
    
    // Timestamp field with SQL function
    createdAt: text('created_at')
      .default(sql`(datetime('now'))`)
      .notNull()
    
    // Foreign key reference
    userId: integer('user_id')
      .notNull()
      .references(() => usersTable.id, { onDelete: 'cascade' })

    API Reference

    initializeDatabase(schemaData: SchemaData)

    Initializes the database package with your schema data.

    interface SchemaData {
      tables: Record<string, any>;           // Your Drizzle table definitions
      seedData?: Record<string, any[]>;      // Optional seed data
      migrationConfig?: MigrationConfig;     // Optional migration configuration
    }
    
    interface MigrationConfig {
      preserveData?: boolean;    // Preserve existing data (default: true)
      autoMigrate?: boolean;     // Enable auto-migrations (default: true)
      dropTables?: boolean;      // Allow dropping tables (default: false)
    }

    DatabaseSync

    Main database synchronization class.

    // Setup database (create tables, migrate, seed)
    await DatabaseSync.setup();
    
    // Reset database (drop all tables and recreate)
    await DatabaseSync.reset();
    
    // Check if database is ready
    const isReady = await DatabaseSync.isReady();

    SchemaIntrospection

    Comprehensive schema analysis utilities.

    // Get all table names
    const tableNames = SchemaIntrospection.getAllTableNames();
    
    // Get table information
    const tableInfo = SchemaIntrospection.getTableInfo('users');
    
    // Get schema statistics
    const stats = SchemaIntrospection.getSchemaStats();
    
    // Validate schema
    const validation = await SchemaIntrospection.validateSchema();

    DatabaseMigrations

    Migration management utilities.

    // Initialize schema
    await DatabaseMigrations.initializeSchema();
    
    // Check table existence
    const existence = await DatabaseMigrations.checkTableExistence();
    
    // Validate schema
    const validation = await DatabaseMigrations.validateSchema();

    DatabaseSeeds

    Database seeding utilities.

    // Seed database
    await DatabaseSeeds.seedDatabase();
    
    // Check if database has data
    const hasData = await DatabaseSeeds.hasData();
    
    // Clear all data
    await DatabaseSeeds.clearAllData();

    Configuration

    Environment Variables

    # Turso Database (optional)
    TURSO_CONNECTION_URL=libsql://your-database.turso.io
    TURSO_AUTH_TOKEN=your-auth-token
    
    # Force local database (for testing)
    FORCE_LOCAL_DB=true
    NODE_ENV=test

    Migration Configuration

    const migrationConfig = {
      preserveData: true,    // Always try to preserve existing data
      autoMigrate: true,     // Automatically handle schema changes
      dropTables: false      // Don't drop tables by default
    };
    
    initializeDatabase({
      tables,
      migrationConfig
    });

    Usage Examples

    Basic Setup

    import { initializeDatabase, DatabaseSync } from 'nanodb-orm';
    import { usersTable, postsTable } from './models';
    
    const tables = {
      users: usersTable,
      posts: postsTable
    };
    
    initializeDatabase({ tables });
    await DatabaseSync.setup();

    With Seed Data

    const seedData = {
      users: [
        { name: 'Alice', age: 25, email: 'alice@example.com' },
        { name: 'Bob', age: 30, email: 'bob@example.com' }
      ],
      posts: [
        { title: 'Hello World', content: 'My first post', userId: 1 },
        { title: 'Second Post', content: 'Another post', userId: 2 }
      ]
    };
    
    initializeDatabase({ tables, seedData });
    await DatabaseSync.setup();

    Custom Migration Config

    const migrationConfig = {
      preserveData: false,   // Allow data loss for development
      autoMigrate: true,     // Enable auto-migrations
      dropTables: true       // Allow dropping tables
    };
    
    initializeDatabase({ 
      tables, 
      migrationConfig 
    });

    Transaction Support (NEW in v0.0.3)

    import { TransactionManager } from 'nanodb-orm';
    
    // Atomic operations
    await TransactionManager.execute(async (db) => {
      await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
      await db.run('INSERT INTO posts (title, content, userId) VALUES (?, ?, ?)', ['Hello', 'World', 1]);
      // All operations succeed or all fail
    });
    
    // Batch operations
    await TransactionManager.executeBatch([
      async (db) => await db.run('INSERT INTO users ...'),
      async (db) => await db.run('INSERT INTO posts ...'),
      async (db) => await db.run('UPDATE stats ...')
    ]);
    
    // Table recreation with transactions
    await TransactionManager.recreateTable('users', async (db) => {
      await db.run('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
    });

    Enhanced Error Handling (NEW in v0.0.3)

    import { ErrorHandler, DatabaseError } from 'nanodb-orm';
    
    try {
      await DatabaseSync.setup();
    } catch (error) {
      if (error instanceof DatabaseError) {
        console.log('Operation:', error.operation);
        console.log('Context:', error.message);
        console.log('Original Error:', error.originalError);
      }
    }
    
    // Non-throwing error handling
    const result = ErrorHandler.handleNonThrowingError(
      someError, 
      'optional-operation', 
      'This operation is optional'
    );

    Thread-Safe Connections (NEW in v0.0.3)

    import { DatabaseConnection } from 'nanodb-orm';
    
    // NEW: Async connection (recommended)
    const db = await DatabaseConnection.getInstance();
    
    // OLD: Synchronous connection (deprecated but still works)
    const db = DatabaseConnection.getInstanceSync();
    
    // Check connection status
    if (DatabaseConnection.isConnected()) {
      console.log('Database is connected');
    }

    Schema Introspection

    import { SchemaIntrospection } from 'nanodb-orm';
    
    // Get comprehensive schema information
    const schemaInfo = SchemaIntrospection.getSchemaStats();
    console.log('Total tables:', schemaInfo.totalTables);
    console.log('Table details:', schemaInfo.tableDetails);
    
    // Validate schema integrity
    const validation = await SchemaIntrospection.validateSchema();
    if (!validation.isValid) {
      console.log('Schema issues:', validation.errors);
    }

    Testing

    The package includes built-in testing utilities:

    import { DatabaseSync } from 'nanodb-orm';
    
    describe('My Tests', () => {
      beforeEach(async () => {
        // Reset database for each test
        await DatabaseSync.reset();
      });
    
      test('should work with clean database', async () => {
        // Your test code here
      });
    });

    Database Support

    Local SQLite

    • Automatically used when Turso credentials are not available
    • Perfect for development and testing
    • File-based storage

    Remote Turso

    • Cloud-hosted SQLite database
    • Requires TURSO_CONNECTION_URL and TURSO_AUTH_TOKEN
    • Production-ready with global replication

    Error Handling

    The package provides comprehensive error handling:

    import { DatabaseError } from 'nanodb-orm';
    
    try {
      await DatabaseSync.setup();
    } catch (error) {
      if (error instanceof DatabaseError) {
        console.log('Database error:', error.message);
        console.log('Operation:', error.operation);
      }
    }

    Contributing

    1. Fork the repository
    2. Create your feature branch (git checkout -b feature/amazing-feature)
    3. Commit your changes (git commit -m 'Add some amazing feature')
    4. Push to the branch (git push origin feature/amazing-feature)
    5. Open a Pull Request

    License

    MIT ยฉ Damilola Alao

    Changelog

    0.0.2

    • Enhanced Documentation: Added comprehensive Drizzle ORM integration examples
    • Real Model Examples: Updated documentation with actual Drizzle table definitions
    • Schema Validation Fix: Fixed table existence validation logic for proper health checks
    • LLM Documentation: Added detailed llm.txt file for AI/LLM integration
    • Column Types Guide: Added complete Drizzle column types and methods documentation
    • Foreign Key Support: Documented foreign key relationships and cascade deletes
    • Type Safety: Enhanced TypeScript integration examples with Drizzle's type inference

    0.0.1

    • Initial release
    • Auto-migration system
    • Schema introspection
    • Multi-database support
    • TypeScript support
    • Testing utilities