Now.js Framework Documentation
StorageManager
StorageManager
ภาพรวม
StorageManager คือระบบจัดการข้อมูลใน Browser ของ Now.js Framework รองรับ IndexedDB, LocalStorage, SessionStorage และ Memory Storage พร้อมฟีเจอร์ขั้นสูง
ใช้เมื่อ:
- ต้องการเก็บข้อมูลในฝั่ง client (offline storage)
- ต้องการ CRUD operations กับ IndexedDB
- ต้องการ caching ข้อมูล
- ต้องการ query และ index ข้อมูล
ทำไมต้องใช้:
- ✅ รองรับ IndexedDB พร้อม object stores และ indexes
- ✅ CRUD operations (add, update, getById, getAll, delete)
- ✅ Query พร้อม filtering, sorting, pagination
- ✅ Automatic caching และ cache invalidation
- ✅ Retry mechanism สำหรับ failed operations
- ✅ Statistics tracking
- ✅ Multiple database และ store support
การใช้งานพื้นฐาน
การติดตั้ง
// เริ่มต้นด้วย stores ที่ต้องการ
await StorageManager.init({
defaultDB: 'my_app',
defaultVersion: 1,
stores: {
users: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'email', keyPath: 'email', options: { unique: true } },
{ name: 'name', keyPath: 'name' }
]
},
products: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'category', keyPath: 'category' },
{ name: 'price', keyPath: 'price' }
]
}
}
});เพิ่มข้อมูล
// เพิ่มข้อมูลเดียว
const userId = await StorageManager.add('users', {
name: 'John Doe',
email: 'john@example.com',
role: 'admin'
});
console.log('User ID:', userId); // 1
// เพิ่มหลายรายการ
const productIds = await StorageManager.add('products', [
{ name: 'iPhone', price: 30000, category: 'electronics' },
{ name: 'MacBook', price: 50000, category: 'electronics' },
{ name: 'Shirt', price: 500, category: 'clothing' }
]);
console.log('Product IDs:', productIds); // [1, 2, 3]อ่านข้อมูล
// อ่านด้วย ID
const user = await StorageManager.getById('users', 1);
console.log(user); // { id: 1, name: 'John Doe', ... }
// อ่านทั้งหมด
const allUsers = await StorageManager.getAll('users');
console.log(allUsers); // [{ id: 1, ... }, { id: 2, ... }]
// อ่านด้วย options
const products = await StorageManager.getAll('products', {
limit: 10,
offset: 0,
direction: 'next'
});อัพเดทข้อมูล
// อัพเดทข้อมูลเดียว (ต้องมี id)
await StorageManager.update('users', {
id: 1,
name: 'John Smith',
email: 'john.smith@example.com',
role: 'admin'
});
// อัพเดทหลายรายการ
await StorageManager.update('products', [
{ id: 1, name: 'iPhone 15', price: 35000 },
{ id: 2, name: 'MacBook Pro', price: 55000 }
]);ลบข้อมูล
// ลบด้วย ID
await StorageManager.delete('users', 1);
// ลบทั้ง store
await StorageManager.clear('users');Query และ Filtering
Query ด้วย Index
// Query ด้วย index
const electronics = await StorageManager.query('products', {
index: 'category',
value: 'electronics'
});
// Query ด้วย range
const expensiveProducts = await StorageManager.query('products', {
index: 'price',
range: IDBKeyRange.lowerBound(10000)
});
// Query ด้วย options
const results = await StorageManager.query('products', {
index: 'category',
value: 'electronics'
}, {
limit: 5,
direction: 'prev' // เรียงจากมากไปน้อย
});Filter Function
// ใช้ filter function สำหรับ complex conditions
const activeAdmins = await StorageManager.getAll('users', {
filter: (user) => user.role === 'admin' && user.active === true
});
// Filter กับ pagination
const paginatedResults = await StorageManager.getAll('products', {
filter: (product) => product.price > 1000,
limit: 10,
offset: 20
});Count
// นับทั้งหมด
const totalUsers = await StorageManager.count('users');
// นับด้วย index
const adminCount = await StorageManager.count('users', {
index: 'role',
value: 'admin'
});
// นับด้วย range
const expensiveCount = await StorageManager.count('products', {
index: 'price',
range: IDBKeyRange.lowerBound(10000)
});Database และ Stores
สร้าง Database
// สร้าง database ใหม่
await StorageManager.createDatabase({
name: 'my_custom_db',
version: 1,
stores: {
logs: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'timestamp', keyPath: 'timestamp' },
{ name: 'level', keyPath: 'level' }
]
}
}
});
// ใช้งานกับ custom database
await StorageManager.add('logs', {
message: 'User logged in',
level: 'info',
timestamp: Date.now()
}, 'my_custom_db');Index Types
stores: {
users: {
keyPath: 'id',
autoIncrement: true,
indexes: [
// Unique index - ค่าต้องไม่ซ้ำ
{ name: 'email', keyPath: 'email', options: { unique: true } },
// Non-unique index
{ name: 'role', keyPath: 'role' },
// Multi-entry index (สำหรับ arrays)
{ name: 'tags', keyPath: 'tags', options: { multiEntry: true } },
// Compound index
{ name: 'name_email', keyPath: ['name', 'email'] }
]
}
}KeyRange
การสร้าง KeyRange
// เท่ากับค่าเดียว
const exactly10 = IDBKeyRange.only(10);
// มากกว่าหรือเท่ากับ
const atLeast10 = IDBKeyRange.lowerBound(10);
// มากกว่า (ไม่รวมขอบ)
const moreThan10 = IDBKeyRange.lowerBound(10, true);
// น้อยกว่าหรือเท่ากับ
const atMost100 = IDBKeyRange.upperBound(100);
// น้อยกว่า (ไม่รวมขอบ)
const lessThan100 = IDBKeyRange.upperBound(100, true);
// ระหว่าง
const between10And100 = IDBKeyRange.bound(10, 100);
// ระหว่าง (ไม่รวมขอบ)
const between10And100Exclusive = IDBKeyRange.bound(10, 100, true, true);ตัวอย่างการใช้ KeyRange
// สินค้าราคา 1000-5000 บาท
const affordable = await StorageManager.query('products', {
index: 'price',
range: IDBKeyRange.bound(1000, 5000)
});
// สินค้าที่ชื่อขึ้นต้นด้วย "A"
const aProducts = await StorageManager.query('products', {
index: 'name',
range: IDBKeyRange.bound('A', 'B', false, true)
});
// Logs ใน 24 ชั่วโมงล่าสุด
const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
const recentLogs = await StorageManager.query('logs', {
index: 'timestamp',
range: IDBKeyRange.lowerBound(oneDayAgo)
});Caching
StorageManager มี built-in caching สำหรับ read operations:
// เปิด caching (ค่าเริ่มต้น)
await StorageManager.init({
cacheLookups: true,
cacheMaxAge: 60 * 1000 // 1 นาที
});
// getById ใช้ cache อัตโนมัติ
const user1 = await StorageManager.getById('users', 1); // อ่านจาก DB
const user2 = await StorageManager.getById('users', 1); // อ่านจาก cache
// Cache ถูก invalidate อัตโนมัติเมื่อ update/delete
await StorageManager.update('users', { id: 1, name: 'New Name' });
const user3 = await StorageManager.getById('users', 1); // อ่านจาก DBการตั้งค่า
await StorageManager.init({
// Database settings
defaultDB: 'now_app_storage',
defaultVersion: 1,
// Retry settings
maxRetries: 3,
retryDelay: 300, // ms
// Default store config
defaultStoreConfig: {
keyPath: 'id',
autoIncrement: true,
indexes: []
},
// Warning threshold
storeSizeWarningThreshold: 50 * 1024 * 1024, // 50MB
// Cache settings
cacheLookups: true,
cacheMaxAge: 60 * 1000, // 1 minute
// Statistics
statistics: {
enabled: true,
trackOperations: true,
trackTiming: true
},
// Store definitions
stores: {
// ... store configs
}
});API อ้างอิง
StorageManager.init(options)
เริ่มต้น StorageManager
| Parameter | Type | Description |
|---|---|---|
options |
object | Configuration options |
Returns: Promise<StorageManager>
StorageManager.add(storeName, data, dbName?)
เพิ่มข้อมูล
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
data |
object/array | ข้อมูลที่จะเพิ่ม |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<any> - ID หรือ array of IDs
const id = await StorageManager.add('users', { name: 'John' });
const ids = await StorageManager.add('users', [{ name: 'John' }, { name: 'Jane' }]);StorageManager.update(storeName, data, dbName?)
อัพเดทข้อมูล
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
data |
object/array | ข้อมูลที่จะอัพเดท (ต้องมี key) |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<boolean>
StorageManager.getById(storeName, id, dbName?)
อ่านข้อมูลด้วย ID
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
id |
any | ID ที่จะอ่าน |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<any|null>
StorageManager.getAll(storeName, options?, dbName?)
อ่านข้อมูลทั้งหมด
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
options.limit |
number | จำนวนสูงสุด |
options.offset |
number | ข้ามกี่รายการ |
options.index |
string | ใช้ index |
options.direction |
string | 'next'/'prev' |
options.filter |
function | Filter function |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<Array>
StorageManager.query(storeName, query, options?, dbName?)
Query ข้อมูล
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
query.index |
string | ชื่อ index |
query.value |
any | ค่าที่ต้องการ match |
query.range |
IDBKeyRange | Key range |
options.limit |
number | จำนวนสูงสุด |
options.direction |
string | ทิศทาง |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<Array>
StorageManager.count(storeName, query?, dbName?)
นับจำนวนข้อมูล
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
query |
object | Query conditions |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<number>
StorageManager.delete(storeName, id, dbName?)
ลบข้อมูล
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
id |
any | ID ที่จะลบ |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<boolean>
StorageManager.clear(storeName, dbName?)
ลบข้อมูลทั้งหมดใน store
| Parameter | Type | Description |
|---|---|---|
storeName |
string | ชื่อ object store |
dbName |
string | ชื่อ database (optional) |
Returns: Promise<boolean>
StorageManager.createDatabase(config)
สร้าง database
| Parameter | Type | Description |
|---|---|---|
config.name |
string | ชื่อ database |
config.version |
number | เวอร์ชัน |
config.stores |
object | Store definitions |
Returns: Promise<IDBDatabase>
StorageManager.isSupported()
ตรวจสอบ IndexedDB support
Returns: boolean
ตัวอย่างการใช้งานจริง
Offline Todo App
// Initialize
await StorageManager.init({
stores: {
todos: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'status', keyPath: 'status' },
{ name: 'dueDate', keyPath: 'dueDate' }
]
}
}
});
// Add todo
async function addTodo(text, dueDate) {
return await StorageManager.add('todos', {
text,
status: 'pending',
dueDate,
createdAt: Date.now()
});
}
// Get pending todos
async function getPendingTodos() {
return await StorageManager.query('todos', {
index: 'status',
value: 'pending'
});
}
// Mark as complete
async function completeTodo(id) {
const todo = await StorageManager.getById('todos', id);
if (todo) {
todo.status = 'completed';
todo.completedAt = Date.now();
await StorageManager.update('todos', todo);
}
}
// Get overdue todos
async function getOverdueTodos() {
const now = Date.now();
return await StorageManager.getAll('todos', {
filter: (todo) => todo.status === 'pending' && todo.dueDate < now
});
}Data Sync
// Initialize with sync tracking
await StorageManager.init({
stores: {
data: {
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'syncStatus', keyPath: 'syncStatus' }
]
}
}
});
// Add with sync status
async function addOfflineData(data) {
return await StorageManager.add('data', {
...data,
syncStatus: 'pending',
createdAt: Date.now()
});
}
// Get unsynchronized data
async function getUnsyncedData() {
return await StorageManager.query('data', {
index: 'syncStatus',
value: 'pending'
});
}
// Mark as synced
async function markSynced(ids) {
const updates = ids.map(id => ({
id,
syncStatus: 'synced',
syncedAt: Date.now()
}));
for (const update of updates) {
const existing = await StorageManager.getById('data', update.id);
await StorageManager.update('data', { ...existing, ...update });
}
}ข้อควรระวัง
⚠️ 1. IndexedDB เป็น Async
// ❌ ไม่ทำงาน - ไม่รอ await
function saveUser(user) {
StorageManager.add('users', user);
console.log('Saved!'); // อาจยังไม่ save จริง
}
// ✅ ใช้ async/await
async function saveUser(user) {
await StorageManager.add('users', user);
console.log('Saved!');
}⚠️ 2. ต้องมี Key เมื่อ Update
// ❌ Error - ไม่มี id
await StorageManager.update('users', { name: 'John' });
// ✅ ต้องมี key (keyPath ที่กำหนดไว้)
await StorageManager.update('users', { id: 1, name: 'John' });⚠️ 3. Index ต้องมีอยู่ก่อน Query
// ❌ Error - index ไม่มี
await StorageManager.query('users', { index: 'nonexistent', value: 'x' });
// ✅ ประกาศ index ตอน init
stores: {
users: {
indexes: [
{ name: 'email', keyPath: 'email' }
]
}
}⚠️ 4. Browser Storage Limits
// ตรวจสอบพื้นที่
try {
const estimate = await navigator.storage.estimate();
console.log(`Used: ${estimate.usage} / ${estimate.quota}`);
} catch (e) {
console.log('Storage estimate not available');
}เอกสารที่เกี่ยวข้อง
- StateManager - Global state management
- ServiceWorkerManager - Offline support
- SyncManager - Data synchronization