IndexedDbClient
A simple and powerful IndexedDB client for working with IndexedDB in the browser. Designed as an ORM-like interface that makes IndexedDB operations intuitive and type-safe.
🇷🇺 Читать на русском языке
Features
- 🚀 Simple API - Intuitive ORM-like interface
- 🔒 Type Safety - Full TypeScript support with generics
- 📊 Indexed Queries - Support for custom indexes and range queries
- 🔄 Promise-based - All operations return promises
- 🎯 Flexible Configuration - Multiple ways to configure the client
- 🛡️ Error Handling - Comprehensive error handling and validation
Installation
npm install @azarov-serge/indexed-db-client
Quick Start
import { IndexedDbClient } from '@azarov-serge/indexed-db-client';
type Task = {
id: number;
name: string;
isDone: boolean;
createdAt: Date;
};
const config = {
dbName: 'myApp',
dbVersion: 1,
storageNames: ['tasks'] as const,
storeNameToIndexes: {
tasks: [
{ index: 'tasksName', key: 'name' },
{ index: 'tasksCreatedAt', key: 'createdAt' }
]
}
};
const client = new IndexedDbClient(config);
await client.init();
const taskId = await client.from('tasks').insert({
name: 'Learn IndexedDB',
isDone: false,
createdAt: new Date()
});
Table of Contents
Configuration
1. Define Types
First, define your storage names and data types:
export type StorageName = 'tasks' | 'categories';
export type Task = {
id: number;
name: string;
isDone: boolean;
createdAt: Date;
categoryId?: number;
};
export type Category = {
id: number;
name: string;
color: string;
};
export type TaskIndexName = 'tasksName' | 'tasksCreatedAt' | 'tasksCategoryId';
export type CategoryIndexName = 'categoriesName';
2. Create Configuration
import { IDBConfig } from '@azarov-serge/indexed-db-client';
import { StorageName, TaskIndexName, CategoryIndexName } from './types';
export const indexedDbConfig: IDBConfig<StorageName, TaskIndexName | CategoryIndexName> = {
dbName: 'myApp',
dbVersion: 1,
storageNames: ['tasks', 'categories'],
storeNameToIndexes: {
tasks: [
{ index: 'tasksName', key: 'name' },
{ index: 'tasksCreatedAt', key: 'createdAt' },
{ index: 'tasksCategoryId', key: 'categoryId' }
],
categories: [
{ index: 'categoriesName', key: 'name', unique: true }
]
}
};
3. Initialize Client
You have two ways to configure the client:
Method 1: Constructor Configuration
const client = new IndexedDbClient(indexedDbConfig);
await client.init();
Method 2: Separate Configuration
const client = new IndexedDbClient();
await client.configure(indexedDbConfig).init();
API Reference
Constructor
new IndexedDbClient<StorageName, IndexName>(config?)
config
(optional): Initial configuration object
Methods
configure(config)
Configure the client after instantiation.
client.configure(config).init();
init()
Initialize the database connection.
await client.init();
from(storageName)
Select a storage to work with. Returns the client for chaining.
client.from('tasks')
insert<T>(data)
Insert a new record. Returns the generated ID.
const id = await client.from('tasks').insert<Task>({
name: 'New Task',
isDone: false,
createdAt: new Date()
});
select<T>(params?)
Select records from storage.
const allTasks = await client.from('tasks').select<Task>();
const tasks = await client.from('tasks').select<Task>({
key: 'tasksName',
value: 'Task Name',
count: 10,
orderBy: 'desc'
});
Select Parameters:
key
: Index name or 'id' for primary key
value
: Value to search for or IDBKeyRange
count
: Limit number of results
orderBy
: 'asc' (default) or 'desc'
update<T>(data)
Update an existing record.
await client.from('tasks').update<Task>({
id: 1,
name: 'Updated Task',
isDone: true,
createdAt: new Date()
});
delete(params)
Delete records by ID or index.
await client.from('tasks').delete({ key: 'id', value: 1 });
await client.from('tasks').delete({
key: 'tasksName',
value: 'Task Name'
});
createStorage(storageName)
Create a new storage (object store).
await client.createStorage('newStorage');
deleteStorage(storageName)
Delete a storage.
await client.deleteStorage('tasks');
deleteDb()
Delete the entire database.
await client.deleteDb();
Properties
isInited
Check if the client is initialized.
if (client.isInited) {
}
Examples
Basic CRUD Operations
import { IndexedDbClient } from '@azarov-serge/indexed-db-client';
const client = new IndexedDbClient(config);
await client.init();
const taskId = await client.from('tasks').insert({
name: 'Learn TypeScript',
isDone: false,
createdAt: new Date()
});
const task = await client.from('tasks').select<Task>({
key: 'id',
value: taskId
});
await client.from('tasks').update({
id: taskId,
name: 'Learn TypeScript (Updated)',
isDone: true,
createdAt: new Date()
});
await client.from('tasks').delete({ key: 'id', value: taskId });
Advanced Queries
const recentTasks = await client
.from('tasks')
.select<Task>({ orderBy: 'desc' });
const firstFive = await client
.from('tasks')
.select<Task>({ count: 5 });
const tasksByName = await client
.from('tasks')
.select<Task>({
key: 'tasksName',
value: 'Learn TypeScript'
});
const recentTasks = await client
.from('tasks')
.select<Task>({
key: 'tasksCreatedAt',
value: IDBKeyRange.lowerBound(new Date('2024-01-01'))
});
const tasksInRange = await client
.from('tasks')
.select<Task>({
key: 'tasksCreatedAt',
value: IDBKeyRange.bound(
new Date('2024-01-01'),
new Date('2024-12-31')
)
});
const tasksByCategory = await client
.from('tasks')
.select<Task>({
key: 'tasksCategoryId',
value: 1
});
Working with Multiple Storages
const categoryId = await client.from('categories').insert({
name: 'Work',
color: '#ff0000'
});
const taskId = await client.from('tasks').insert({
name: 'Complete project',
isDone: false,
createdAt: new Date(),
categoryId: categoryId
});
const categories = await client.from('categories').select<Category>();
const workTasks = await client
.from('tasks')
.select<Task>({
key: 'tasksCategoryId',
value: categoryId
});
Error Handling
try {
await client.init();
const taskId = await client.from('tasks').insert({
name: 'Task',
isDone: false,
createdAt: new Date()
});
console.log('Task created with ID:', taskId);
} catch (error) {
console.error('Error:', error);
if (error instanceof Error) {
switch (error.message) {
case 'Config is not exist':
console.error('Client not configured');
break;
case 'DB is not init':
console.error('Database not initialized');
break;
case 'No storage selected':
console.error('No storage selected');
break;
default:
console.error('Unknown error:', error.message);
}
}
}
Advanced Usage
Custom Indexes
const config = {
storeNameToIndexes: {
users: [
{ index: 'usersEmail', key: 'email', unique: true },
{ index: 'usersUsername', key: 'username', unique: true }
]
}
};
const user = await client
.from('users')
.select<User>({
key: 'usersEmail',
value: 'user@example.com'
});
Database Versioning
const config = {
dbName: 'myApp',
dbVersion: 2,
};
await client.init();
Bulk Operations
const tasks = [
{ name: 'Task 1', isDone: false, createdAt: new Date() },
{ name: 'Task 2', isDone: false, createdAt: new Date() },
{ name: 'Task 3', isDone: false, createdAt: new Date() }
];
const ids = await Promise.all(
tasks.map(task => client.from('tasks').insert(task))
);
await Promise.all(
ids.map(id => client.from('tasks').delete({ key: 'id', value: id }))
);
Browser Support
This library works in all modern browsers that support IndexedDB:
- Chrome 23+
- Firefox 16+
- Safari 10+
- Edge 12+
- Internet Explorer 10+
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
🇷🇺 Читать на русском языке