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-ormQuick 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 numberstext()- Text stringsreal()- Floating point numbersblob()- 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=testMigration 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_URLandTURSO_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
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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.txtfile 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