IndexedDB & NX.ref
— NexaStorage
Operasi store lokal lewat NX.Storage(), dDB.indexedDB, dan
NX.ref — auto/manual mode, CRUD, query, watch, refresh,
ukuran, export/import. Melengkapi
buckets & Storage.
Sebelumnya: NexaModels — berikutnya:
NexaForge.
Di cuplikan berikut, biasanya const dDB = NX.Storage(); sudah tersedia. API utama ada di
NX.ref setelah dDB.indexedDB.init(...).
Import & default stores
Daftar nama store bawaan yang sering dipakai bersama metadata / aktivitas:
const defaultStores = [
"nexaStore",
"bucketsStore",
"folderStructure",
"activityLogs",
"metadata",
"recycleBin",
"fileContents",
"fileSettings",
"presentations",
];1. Inisialisasi database
const dDB = NX.Storage();1.1 Auto mode
async function initAutoMode() {
try {
await dDB.indexedDB.init("MyAppDB", 1);
console.log("Auto mode OK", NX.ref.getInfo());
} catch (error) {
console.error("Auto mode init failed:", error);
}
}1.2 Manual mode
async function initManualMode() {
try {
await dDB.indexedDB.init("MyAppDB", 1, ["users", "products", "orders"]);
console.log("Stores:", NX.ref.getInfo().availableStores);
} catch (error) {
console.error("Manual mode init failed:", error);
}
}1.3 Tambah store dinamis
async function addNewStores() {
try {
await dDB.indexedDB.init("MyAppDB", 1, ["users"]);
await NX.ref.addStores(["products", "categories"]);
console.log("Updated stores:", NX.ref.getInfo().availableStores);
} catch (error) {
console.error("addStores failed:", error);
}
}2. Operasi CRUD dasar
2.1 Create / update
async function createUpdateData() {
try {
const userId = await NX.ref.set("users", {
id: "user_001",
name: "John Doe",
email: "john@example.com",
role: "admin",
profile: { avatar: "avatar.jpg", bio: "Software Developer" },
});
console.log("Created id:", userId);
await NX.ref.set("users", {
id: "user_001",
name: "John Smith",
email: "john.smith@example.com",
role: "admin",
profile: { avatar: "new_avatar.jpg", bio: "Senior Software Developer" },
});
console.log("Updated OK");
} catch (error) {
console.error("set failed:", error);
}
}2.2 Read
async function readData() {
try {
const user = await NX.ref.get("users", "user_001");
const allUsers = await NX.ref.getAll("users");
const hasUsersStore = NX.ref.hasStore("users");
console.log(user, allUsers.data, hasUsersStore);
} catch (error) {
console.error("read failed:", error);
}
}2.3 Delete
async function deleteData() {
try {
const result = await NX.ref.delete("users", "user_001");
const deletedUser = await NX.ref.get("users", "user_001");
console.log(result, deletedUser);
} catch (error) {
console.error("delete failed:", error);
}
}3. Operasi data lanjutan
3.1 Get latest
async function getLatestData() {
try {
await NX.ref.set("posts", { id: "post_1", title: "First Post", content: "Content 1" });
await new Promise((r) => setTimeout(r, 100));
await NX.ref.set("posts", { id: "post_2", title: "Second Post", content: "Content 2" });
await new Promise((r) => setTimeout(r, 100));
await NX.ref.set("posts", { id: "post_3", title: "Latest Post", content: "Content 3" });
const latestPost = await NX.ref.getLatest("posts", 1);
const latest2Posts = await NX.ref.getLatest("posts", 2);
console.log(latestPost.data, latest2Posts.data);
} catch (error) {
console.error("getLatest failed:", error);
}
}3.2 Get oldest
async function getOldestData() {
try {
const oldestPost = await NX.ref.getOldest("posts", 1);
const oldest2Posts = await NX.ref.getOldest("posts", 2);
console.log(oldestPost.data, oldest2Posts.data);
} catch (error) {
console.error("getOldest failed:", error);
}
}3.3 Date range
async function getByDateRange() {
try {
const startDate = new Date("2025-01-01");
const endDate = new Date("2025-12-31");
const postsInRange = await NX.ref.getByDateRange("posts", startDate, endDate);
console.log(postsInRange);
} catch (error) {
console.error("date range failed:", error);
}
}3.4 Search
async function searchData() {
try {
const allFields = await NX.ref.search("posts", "latest");
const titleOnly = await NX.ref.search("posts", "first", ["title"]);
console.log(allFields, titleOnly);
} catch (error) {
console.error("search failed:", error);
}
}4. Real-time & refresh
4.1 Watch
async function watchDataChanges() {
try {
const unwatch = NX.ref.watch("users", (event) => {
console.log({
store: event.storeName,
changeType: event.changeType,
data: event.data,
timestamp: event.timestamp,
});
});
await NX.ref.set("users", { id: "user_002", name: "Jane Doe" });
await NX.ref.set("users", { id: "user_002", name: "Jane Smith" });
await NX.ref.delete("users", "user_002");
setTimeout(() => {
unwatch();
}, 10000);
} catch (error) {
console.error("watch failed:", error);
}
}4.2 Auto refresh dari API
async function setupAutoRefresh() {
try {
const pollId = await NX.ref.setupAutoRefresh("products", {
strategy: "polling",
endpoint: async () => {
const response = await fetch("/api/products");
return await response.json();
},
pollInterval: 30000,
immediate: true,
});
console.log("pollId", pollId);
setTimeout(() => NX.ref.stopPolling("products"), 300000);
} catch (error) {
console.error("setupAutoRefresh failed:", error);
}
}4.3 Manual refresh
async function manualRefresh() {
try {
await NX.ref.refreshTable("products", async () => {
const response = await fetch("/api/products");
return await response.json();
});
await NX.ref.refreshTable();
} catch (error) {
console.error("manualRefresh failed:", error);
}
}4.4 Simple refresh
async function simpleRefresh() {
try {
await NX.ref.set("users", { id: "user_1", name: "John", status: "active" });
await NX.ref.refresh();
await NX.ref.refresh("https://api.example.com/data", {
onProgress: (progress) =>
console.log(`Progress: ${progress.completed}/${progress.total}`),
});
await NX.ref.refreshTable("users", "https://api.example.com/users");
await NX.ref.refreshTable("products");
} catch (error) {
console.error("simpleRefresh failed:", error);
}
}4.5 Batch operations
async function batchOperations() {
try {
const batchData = [
{ id: "product_1", name: "Laptop", price: 1000 },
{ id: "product_2", name: "Mouse", price: 25 },
{ id: "product_3", name: "Keyboard", price: 75 },
];
await NX.ref.batchUpdate("products", batchData, {
clearFirst: true,
batchSize: 100,
onProgress: (progress) => console.log(`Batch: ${progress.percentage}%`),
});
} catch (error) {
console.error("batchUpdate failed:", error);
}
}5. Ukuran & cleanup
5.1 Monitoring
async function monitorDatabaseSize() {
try {
const sizeInfo = await NX.ref.getDatabaseSize();
const storeSize = await NX.ref.getStoreSize("users");
const dataSize = await NX.ref.getDataSize("users", "user_001");
const storageUsage = await NX.ref.getStorageUsage();
console.log(sizeInfo, storeSize, dataSize, storageUsage);
} catch (error) {
console.error("size monitoring failed:", error);
}
}5.2 Auto monitor
async function startSizeMonitoring() {
try {
const stopMonitoring = NX.ref.monitorSize(30000);
setTimeout(() => stopMonitoring(), 300000);
} catch (error) {
console.error("monitorSize failed:", error);
}
}5.3 Cleanup by size
async function cleanupBySize() {
try {
const cleanupResult = await NX.ref.cleanupBySize("logs", 10);
console.log(cleanupResult);
} catch (error) {
console.error("cleanupBySize failed:", error);
}
}6. Export / import
6.1 Export
async function exportData() {
try {
const exportResult = await NX.ref.export();
const specificExport = await NX.ref.export(["users", "products"], {
encrypt: true,
includeMetadata: true,
onProgress: (progress) =>
console.log(`Export: ${progress.completed}/${progress.total}`),
});
await NX.ref.exportToFile("my_backup.json", ["users"], {
encrypt: false,
includeMetadata: true,
});
console.log(exportResult, specificExport);
} catch (error) {
console.error("export failed:", error);
}
}6.2 Import
async function importData() {
try {
const payload = {
metadata: {
dbName: "MyAppDB",
exportDate: new Date().toISOString(),
stores: ["users"],
},
data: {
users: [
{ id: "user_003", name: "Alice Johnson", email: "alice@example.com" },
{ id: "user_004", name: "Bob Wilson", email: "bob@example.com" },
],
},
};
const importResult = await NX.ref.import(payload, {
decrypt: false,
overwrite: true,
onProgress: (progress) =>
console.log(`Import: ${progress.completed}/${progress.total}`),
});
console.log(importResult);
} catch (error) {
console.error("import failed:", error);
}
}6.3 Import dari berkas
async function importFromFile() {
try {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = ".json";
fileInput.onchange = async (event) => {
const file = event.target.files[0];
if (file) {
const importResult = await NX.ref.importFromFile(file, {
decrypt: false,
overwrite: true,
});
console.log(importResult);
}
};
fileInput.click();
} catch (error) {
console.error("importFromFile failed:", error);
}
}7. Safe operations
Antarmuka safe() mengurangi throw pada operasi umum:
async function safeOperations() {
try {
const safe = NX.ref.safe();
const user = await safe.get("nonexistent_store", "user_001");
const allData = await safe.getAll("nonexistent_store");
await safe.set("new_store", { id: "item_001", name: "New Item" });
const stores = safe.listStores();
console.log(user, allData, stores);
} catch (error) {
console.error("safeOperations failed:", error);
}
}8. Info, reset & cleanup resource
8.1 Info & status
async function getDatabaseInfo() {
try {
const info = NX.ref.getInfo();
const realtimeStatus = NX.ref.getRealtimeStatus();
const hasUsers = NX.ref.hasStore("users");
console.log(info, realtimeStatus, hasUsers);
} catch (error) {
console.error("getInfo failed:", error);
}
}8.2 Reset database (hapus semua data — hati-hati)
async function resetDatabase() {
try {
await NX.ref.resetDatabase();
await initAutoMode();
} catch (error) {
console.error("resetDatabase failed:", error);
}
}8.3 Bersihkan resource real-time
async function cleanupResources() {
NX.ref.cleanup();
}9. Sinkron dengan localStorage
Lewat instance Storage (bukan hanya NX.ref):
async function syncWithLocalStorage() {
const storage = NX.Storage();
try {
const toIdb = await storage.sync.localStorageToIndexedDB({
prefix: "app_",
storeName: "localStorage_backup",
overwrite: true,
clearTarget: false,
});
const toLs = await storage.sync.indexedDBToLocalStorage("users", {
prefix: "backup_",
overwrite: true,
clearTarget: false,
});
const both = await storage.sync.bidirectional("users", {
localStoragePrefix: "sync_",
strategy: "localStorage",
});
console.log(toIdb, toLs, both);
} catch (error) {
console.error("sync failed:", error);
}
}10. Bandingkan ukuran storage
Gunakan helper storage.compare pada instance NX.Storage().
async function compareStorages() {
const storage = NX.Storage();
try {
const sizes = await storage.compare.getSizes();
const health = await storage.compare.healthCheck();
console.log(sizes, health);
} catch (error) {
console.error("compare failed:", error);
}
}11. Best practices & contoh
Contoh manajemen user
async function userManagementExample() {
try {
const users = [
{ id: "admin_001", name: "Admin User", role: "admin", email: "admin@example.com" },
{ id: "user_001", name: "Regular User", role: "user", email: "user@example.com" },
{ id: "mod_001", name: "Moderator", role: "moderator", email: "mod@example.com" },
];
for (const user of users) {
await NX.ref.set("users", user);
}
const unwatch = NX.ref.watch("users", (event) => {
console.log(event.changeType, event.data.name);
});
const adminUsers = await NX.ref.search("users", "admin", ["role", "name"]);
const latestUser = await NX.ref.getLatest("users", 1);
await NX.ref.set("users", {
id: "user_001",
name: "Updated Regular User",
role: "user",
email: "updated.user@example.com",
lastLogin: new Date().toISOString(),
});
await NX.ref.exportToFile("users_backup.json", ["users"]);
const userStoreSize = await NX.ref.getStoreSize("users");
console.log(adminUsers, latestUser.data, userStoreSize);
setTimeout(() => unwatch(), 5000);
} catch (error) {
console.error("userManagementExample failed:", error);
}
}Contoh katalog (ringkas)
async function ecommerceExample() {
try {
const products = [
{ id: "prod_001", name: "Laptop Gaming", category: "Electronics", price: 1500, stock: 10 },
{ id: "prod_002", name: "Wireless Mouse", category: "Accessories", price: 50, stock: 100 },
];
await NX.ref.batchUpdate("products", products, { clearFirst: true });
const categories = [
{ id: "cat_001", name: "Electronics", description: "Electronic devices" },
{ id: "cat_002", name: "Accessories", description: "Computer accessories" },
];
await NX.ref.batchUpdate("categories", categories, { clearFirst: true });
const laptops = await NX.ref.search("products", "laptop", ["name", "category"]);
const allProducts = await NX.ref.getAll("products");
const expensive = allProducts.data.filter((p) => p.price > 100);
const unwatchProducts = NX.ref.watch("products", (event) => {
if (event.changeType === "update") console.log(event.data.name, event.data.stock);
});
await NX.ref.set("products", {
id: "prod_001",
name: "Laptop Gaming",
category: "Electronics",
price: 1400,
stock: 8,
});
await NX.ref.exportToFile("catalog_backup.json", ["products", "categories"]);
setTimeout(() => unwatchProducts(), 3000);
console.log(laptops, expensive.length);
} catch (error) {
console.error("ecommerceExample failed:", error);
}
}12. Error handling & debugging
Fallback init, cek hasil get, batch dengan skip item rusak, cek realtime:
async function errorHandlingExamples() {
try {
try {
await dDB.indexedDB.init("MyAppDB", 1);
} catch (error) {
console.warn("IndexedDB gagal, fallback localStorage");
const storage = NX.Storage();
return storage.localStorage;
}
try {
const user = await NX.ref.get("users", "nonexistent_user");
console.log(user || "No user");
} catch (error) {
console.error("get user:", error.message);
}
const batchData = [
{ id: "valid_001", name: "Valid Item 1" },
{},
{ id: "valid_002", name: "Valid Item 2" },
];
for (const item of batchData) {
try {
if (item.id && item.name) {
await NX.ref.set("items", item);
} else {
console.warn("skip invalid", item);
}
} catch (error) {
console.error("item save:", error.message);
}
}
const realtimeStatus = NX.ref.getRealtimeStatus();
if (!realtimeStatus.broadcastChannel) {
console.warn("Cross-tab sync tidak tersedia");
}
} catch (error) {
console.error("errorHandlingExamples failed:", error);
}
}13. Optimasi performa
Batch besar, query terarah, ukuran store, batasi watcher:
async function performanceOptimization() {
try {
const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
id: `item_${i}`,
name: `Item ${i}`,
data: `Large data content ${i}`.repeat(100),
}));
console.time("Batch Insert");
await NX.ref.batchUpdate("large_items", largeDataset, {
batchSize: 100,
onProgress: (progress) => {
if (progress.percentage % 20 === 0) console.log(`${progress.percentage}%`);
},
});
console.timeEnd("Batch Insert");
console.time("Latest Query");
const latest10 = await NX.ref.getLatest("large_items", 10);
console.timeEnd("Latest Query");
console.log(latest10.data.length);
const sizeInfo = await NX.ref.getStoreSize("large_items");
if (parseFloat(sizeInfo.sizeMB) > 5) {
await NX.ref.cleanupBySize("large_items", 3);
}
let changeCount = 0;
const unwatch = NX.ref.watch("large_items", () => {
changeCount++;
});
setTimeout(() => unwatch(), 5000);
} catch (error) {
console.error("performanceOptimization failed:", error);
}
}14. Testing & validasi
Contoh merangkai tes: setiap fn mengacu ke fungsi contoh yang sudah didefinisikan di modul Anda.
async function runTests() {
const tests = [
{ name: "Auto Mode Initialization", fn: initAutoMode },
{ name: "Manual Mode Initialization", fn: initManualMode },
{ name: "CRUD Operations", fn: createUpdateData },
{ name: "Data Queries", fn: getLatestData },
{ name: "Search Functionality", fn: searchData },
{ name: "Real-time Watching", fn: watchDataChanges },
{ name: "Size Management", fn: monitorDatabaseSize },
{ name: "Export/Import", fn: exportData },
{ name: "Safe Operations", fn: safeOperations },
{ name: "Cross-storage Sync", fn: syncWithLocalStorage },
{ name: "User Management Example", fn: userManagementExample },
{ name: "E-commerce Example", fn: ecommerceExample },
];
const results = [];
for (const test of tests) {
try {
await test.fn();
results.push({ name: test.name, status: "PASSED" });
} catch (error) {
results.push({ name: test.name, status: "FAILED", error: error.message });
}
}
const passed = results.filter((r) => r.status === "PASSED").length;
console.log("Passed", passed, "/", results.length);
return results;
}15. Update field & merge
15.1 Beberapa field
await NX.ref.set("users", {
id: "user_001",
name: "John Doe",
email: "john@example.com",
age: 30,
role: "user",
profile: { avatar: "avatar.jpg", bio: "Software Developer", location: "Jakarta" },
settings: { theme: "light", notifications: true, language: "id" },
});
await NX.ref.updateFields("users", "user_001", {
email: "john.new@example.com",
age: 31,
role: "admin",
});15.2 Satu field
await NX.ref.updateField("users", "user_001", "email", "updated.email@example.com");
await NX.ref.updateField("users", "user_001", "age", 32);
await NX.ref.updateField("users", "user_001", "role", "super_admin");15.3 Field bersarang (notasi titik)
await NX.ref.updateNestedField("users", "user_001", "profile.bio", "Senior Software Developer");
await NX.ref.updateNestedField("users", "user_001", "profile.location", "Bandung");
await NX.ref.updateNestedField("users", "user_001", "settings.theme", "dark");
await NX.ref.updateNestedField("users", "user_001", "settings.notifications", false);15.4 Merge shallow & deep
await NX.ref.mergeData("users", "user_001", {
status: "active",
lastLogin: new Date().toISOString(),
loginCount: 1,
});
await NX.ref.mergeData(
"users",
"user_001",
{
profile: {
bio: "Expert Software Developer",
skills: ["JavaScript", "React", "Node.js"],
},
settings: { language: "en", timezone: "Asia/Jakarta" },
},
{ deepMerge: true }
);
await NX.ref.mergeData(
"users",
"user_002",
{
name: "Jane Smith",
email: "jane@example.com",
profile: { bio: "UI/UX Designer", location: "Jakarta" },
},
{ createIfNotExists: true }
);
Ada pula batchUpdateFields, contoh partial e-commerce / profil, dan suite tes khusus partial update di
implementasi NexaStorage Anda — pola pemakaian sama: store, id rekaman, objek field atau opsi
deepMerge / createIfNotExists.
16. Export fungsi untuk pakai eksternal
Dari modul tunggal, re-export skenario uji / demo:
export {
initAutoMode,
initManualMode,
addNewStores,
createUpdateData,
readData,
deleteData,
getLatestData,
getOldestData,
getByDateRange,
searchData,
watchDataChanges,
setupAutoRefresh,
manualRefresh,
simpleRefresh,
batchOperations,
monitorDatabaseSize,
startSizeMonitoring,
cleanupBySize,
exportData,
importData,
importFromFile,
safeOperations,
getDatabaseInfo,
resetDatabase,
cleanupResources,
syncWithLocalStorage,
compareStorages,
userManagementExample,
ecommerceExample,
errorHandlingExamples,
performanceOptimization,
runTests,
};18. Multi store / multi database
18.1 Banyak store dalam satu DB
async function multipleStoresExample() {
try {
await dDB.indexedDB.init("MyAppDB", 1, [
"users",
"products",
"orders",
"categories",
"reviews",
"settings",
"logs",
]);
await NX.ref.set("users", { id: "user_1", name: "John Doe", role: "admin" });
await NX.ref.set("products", { id: "prod_1", name: "Laptop", price: 1000 });
await NX.ref.set("orders", {
id: "order_1",
userId: "user_1",
productId: "prod_1",
total: 1000,
});
const user = await NX.ref.get("users", "user_1");
const product = await NX.ref.get("products", "prod_1");
console.log(user, product);
} catch (error) {
console.error("multipleStoresExample failed:", error);
}
}18.2 Auto mode / store runtime
async function dynamicStoreCreation() {
try {
await dDB.indexedDB.init("DynamicDB", 1);
await NX.ref.set("customers", { id: "cust_1", name: "Alice", email: "alice@example.com" });
await NX.ref.set("suppliers", { id: "supp_1", name: "Tech Corp", contact: "tech@corp.com" });
console.log(NX.ref.listStores ? NX.ref.listStores() : NX.ref.getInfo().availableStores);
} catch (error) {
console.error("dynamicStoreCreation failed:", error);
}
}20. Duplikasi data
NX.ref.duplicate(store, sourceId, newId) menyalin rekaman dengan id baru.
async function basicDuplicateExample() {
try {
await NX.ref.set("users", {
id: "user_001",
name: "John Doe",
email: "john@example.com",
role: "admin",
profile: { avatar: "avatar.jpg", bio: "Software Developer", location: "Jakarta" },
settings: { theme: "dark", notifications: true, language: "id" },
});
const duplicateResult = await NX.ref.duplicate("users", "user_001", "user_001_copy");
const original = await NX.ref.get("users", "user_001");
const duplicate = await NX.ref.get("users", "user_001_copy");
console.log(duplicateResult, original, duplicate);
} catch (error) {
console.error("duplicate failed:", error);
}
}19. Menambah tabel ke DB yang sudah ada
addStores() menambah nama store tanpa init ulang penuh:
async function addNewTablesToExistingDB() {
try {
await dDB.indexedDB.init("MyAppDB", 1, ["users", "products", "orders"]);
await NX.ref.addStores(["categories", "reviews", "settings", "notifications", "logs"]);
await NX.ref.set("categories", {
id: "cat_1",
name: "Electronics",
description: "Electronic devices",
});
await NX.ref.set("reviews", {
id: "rev_1",
productId: "prod_1",
rating: 5,
comment: "Excellent!",
});
const category = await NX.ref.get("categories", "cat_1");
console.log(category, NX.ref.getInfo().availableStores);
} catch (error) {
console.error("addNewTablesToExistingDB failed:", error);
}
}Berkas implementasi
Referensi integrasi: assets/modules/nxdom.js (inisialisasi NX.ref), modul Storage /
IndexedDB di assets/modules sesuai struktur proyek Anda.