nexabase-console
v2.2.0
Published
SDK Client resmi untuk NexaBase: Platform Sinkronisasi NoSQL, Realtime, File Storage, & Autentikasi Offline-First.
Maintainers
Readme
NexaBase JavaScript SDK (nexabase-console)
SDK Client Resmi untuk NexaBase: Platform sinkronisasi database modern yang menyatukan fungsionalitas Firestore NoSQL, Real-Time Database, File Storage, dan Edge Authentication dengan dukungan Offline-First (IndexedDB) serta Passwordless OTP.
🚀 Fitur Utama
- ⚡ Real-Time Synchronization & Anti-Flicker Engine: Sinkronisasi data real-time dengan latency rendah menggunakan Server-Sent Events (SSE) yang dilengkapi Deep Comparison Data Hashing untuk mencegah re-render/flicker komponen UI saat sync.
- 🚀 Latency Compensation (Optimistic UI & 0ms Local Render): Penulisan data (
setDoc,updateDoc,deleteDoc,writeBatch) langsung memperbarui cache lokal IndexedDB dan memicuonSnapshotsecara instan (0ms) sebelum konfirmasi jaringan selesai. - 🔄 Delta Updates (
docChanges()): Dukungan native pelacakan perubahan inkremental (added,modified,removed) padaQuerySnapshotuntuk manipulasi elemen UI secara presisi tanpa mengunduh ulang seluruh dataset. - 🔒 Concurrency Control & Transactions (
runTransaction): Transaksi dua arah (Read-Before-Write) atomik dengan kontrol konkurensi (Optimistic Locking & Automatic Retry) untuk mencegah race conditions pada pemotongan stok, kuota, atau transaksi finansial. - 📦 Offline-First & Auto-Sync: Sinkronisasi state lokal secara otomatis menggunakan IndexedDB. Data tetap tersimpan di browser jika koneksi terputus dan akan disinkronkan ke server secara otomatis saat kembali online.
- 🔥 Modular Firestore API & TypeScript Generics: Desain API modular modern mirip Firebase SDK (v9+) dengan dukungan penuh parameter tipe generik (TypeScript Generics) untuk menjamin tipe data yang aman (type-safe) secara end-to-end.
- 🔑 Secure Auth & Passwordless OTP: Dukungan penuh registrasi, login email/sandi, serta OTP instan via SMTP yang aman.
- 📂 File Storage Integration: Unggah aset media dan file blob ke cloud storage dengan satu perintah.
📦 Instalasi
Instal package melalui npm atau yarn:
npm install nexabase-console
# atau
yarn add nexabase-console🛠️ Cara Penggunaan & Contoh Kode
1. Inisialisasi Aplikasi
import { initializeApp, getFirestore } from 'nexabase-console';
const app = initializeApp({
projectId: 'PROYEK_NEXABASE_ID_ANDA',
apiKey: 'NEXABASE_API_KEY_ANDA' // Opsional, default endpoint mengarah ke https://db.nexabase.id
});
const db = getFirestore(app);🛡️ Keamanan & Autentikasi Ganda (API Key & Sesi)
Untuk mendukung arsitektur server-side proxy atau direct API key yang sangat fleksibel, SDK secara otomatis mengirimkan token Anda dalam dua format header sekaligus:
Authorization: Bearer <token>: Digunakan untuk validasi token JWT sesi user.x-api-key: <api_key>: Digunakan untuk otentikasi aman tanpa sesi menggunakan API Key proyek.
Hal ini mencegah kegagalan otentikasi (Error 403 Forbidden: Please login) saat memanggil metode di server menggunakan API Key proyek.
⚡ Sinkronisasi Offline & Database-Priority Engine (IndexedDB Cache)
NexaBase SDK mengimplementasikan manajemen cache tangguh yang serupa dengan Firebase untuk menjamin keandalan aplikasi saat jaringan tidak stabil:
- Prioritas Database Utama (
network-firstsecara default): SDK memprioritaskan pengambilan data dari database remote server untuk menjamin kesegaran data (data freshness). - Fast-Fail Network Timeout: Setiap permintaan database remote memiliki batas waktu (timeout) selama 6 detik. Jika jaringan sangat lambat (lie-fi) atau offline, SDK akan gagal cepat dan mengalihkan pembacaan ke IndexedDB local cache secara transparan tanpa menghentikan aplikasi.
- Pembaruan Optimistik Instan: Mutasi data (
setDoc,patchDoc,deleteDoc,writeBatch) langsung ditulis ke IndexedDB secara optimistik. Semua listener snapshot (onSnapshot) akan menerima perubahan secara instan agar UI terasa sangat responsif (snappy). - Heartbeat Polling Outbox (Setiap 10 Detik): Di samping mendengarkan event transisi jaringan
online, SDK menjalankan polling latar belakang setiap 10 detik. Jika koneksi terdeteksi pulih, SDK akan mem-flush antrean mutasi offline secara berurutan (FIFO). - Deduplikasi & Proteksi Kemacetan Antrean: SDK menggunakan kunci idempotensi (
X-Idempotency-Key) untuk mencegah tulisan ganda. Selain itu, jika server membalas dengan kesalahan definitif sisi klien (seperti400 Bad Requestatau403 Forbidden), SDK akan secara otomatis membuang job cacat tersebut dari antrean untuk mencegah antrean macet selamanya.
2. Firestore-like Modular API (NoSQL)
Menulis Data Baru (Set Doc)
import { doc, setDoc } from 'nexabase-console';
const main = async () => {
const profileRef = doc(db, 'users', 'budi_santoso');
const res = await setDoc(profileRef, {
name: 'Budi Santoso',
email: '[email protected]',
age: 28,
hobbies: ['Coding', 'Cycling']
});
console.log("Data berhasil disimpan:", res);
};
main();Memperbarui Sebagian Data (Update Doc)
import { doc, updateDoc } from 'nexabase-console';
const editData = async () => {
const profileRef = doc(db, 'users', 'budi_santoso');
await updateDoc(profileRef, {
age: 29 // Hanya memperbarui field age
});
};Membaca Satu Dokumen (Get Doc)
import { doc, getDoc } from 'nexabase-console';
const readData = async () => {
const profileRef = doc(db, 'users', 'budi_santoso');
const snapshot = await getDoc(profileRef);
if (snapshot.exists()) {
console.log("Isi data:", snapshot.data());
} else {
console.log("Dokumen tidak ditemukan.");
}
};Mendengarkan Perubahan Data secara Real-Time (onSnapshot & docChanges)
import { collection, onSnapshot } from 'nexabase-console';
const listenLiveChats = () => {
const chatsRef = collection(db, 'chats');
// onSnapshot menggunakan Anti-Flicker Engine (Deep Comparison)
// - Langsung memancarkan cache lokal secara instan (0ms) tanpa layar putih
// - Membandingkan hash data server sebelum re-render agar UI tidak berkedip
const unsubscribe = onSnapshot(chatsRef, (querySnapshot) => {
// 1. Ambil rincian pembaruan inkremental (Delta/Diff Updates)
if (querySnapshot.docChanges) {
const changes = querySnapshot.docChanges();
changes.forEach((change) => {
if (change.type === 'added') console.log("Pesan baru:", change.doc.data());
if (change.type === 'modified') console.log("Pesan diedit:", change.doc.data());
if (change.type === 'removed') console.log("Pesan dihapus:", change.doc.id);
});
}
// 2. Iterasi seluruh koleksi
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
});
// Panggil unsubscribe() untuk menghentikan pemantauan realtime
// unsubscribe();
};Agregasi Data & Hitung Dokumen (getCountFromServer / getAggregateFromServer)
Mendukung fungsi agregasi server 1:1 seperti Firebase SDK: getCountFromServer(), getAggregateFromServer(), count(), sum(), dan average().
import {
collection,
query,
where,
getCountFromServer,
getAggregateFromServer,
count,
sum,
average
} from 'nexabase-console';
// 1. Menghitung total dokumen (getCountFromServer)
const productsRef = collection(db, 'products');
const qAvailable = query(productsRef, where('status', '==', 'active'));
const countSnapshot = await getCountFromServer(qAvailable);
console.log("Total produk aktif:", countSnapshot.data().count);
// 2. Agregasi multi-field (getAggregateFromServer)
const orderStats = await getAggregateFromServer(collection(db, 'orders'), {
totalOrders: count(),
totalRevenue: sum('totalAmount'),
avgOrderValue: average('totalAmount')
});
console.log("Jumlah order:", orderStats.data().totalOrders);
console.log("Total pendapatan:", orderStats.data().totalRevenue);
console.log("Rata-rata transaksi:", orderStats.data().avgOrderValue);Menggunakan Atomic Write Batch (Untuk Upload File / Excel Massal)
Jika Anda mengunggah data sekaligus dalam jumlah besar dari file (misal: import 500 resi dari Excel), hindari memanggil setDoc satu per satu di dalam looping. Gunakan fitur writeBatch yang membundel seluruh operasi Anda dalam satu pengiriman jaringan (hingga 500 operasi per eksekusi).
Catatan: Jika alur kerjanya adalah kasir/admin melakukan scan barcode satu-per-satu lalu menekan enter, maka Anda TIDAK PERLU menggunakan writeBatch. Menyimpan data satu-per-satu (setDoc / addDoc) setiap kali dienter adalah cara yang paling benar dan normal untuk kebutuhan realtime. Database dapat dengan mudah menangani 500 scan yang terjadi secara bertahap sepanjang hari.
import { doc, writeBatch } from 'nexabase-console';
const executeBulkUpload = async (listResi) => {
const batch = writeBatch(db);
// ListResi adalah array berisi objek resi. Maksimal 500 per batch.
listResi.forEach((resi) => {
// Referensi dokumen
const docRef = doc(db, 'resi', resi.nomor_resi);
batch.set(docRef, resi);
});
// Eksekusi semua secara atomik (bersamaan dalam 1 request)
const result = await batch.commit();
console.log(`Berhasil mengunggah ${listResi.length} resi secara massal.`);
};Menggunakan Transactions (runTransaction) — Concurrency Control
import { doc, runTransaction } from 'nexabase-console';
async function potongStokAman(productId, variantId, qtyBeli) {
const productRef = doc(db, 'products', productId);
try {
const hasil = await runTransaction(db, async (transaction) => {
// 1. Baca dokumen di dalam transaksi (Read)
const productSnap = await transaction.get(productRef);
if (!productSnap.exists()) {
throw new Error("Produk tidak ditemukan!");
}
const productData = productSnap.data();
const variantIndex = productData.variants?.findIndex((v) => v.id === variantId);
if (variantIndex === -1 || variantIndex === undefined) {
throw new Error("Varian produk tidak ditemukan!");
}
const variant = productData.variants[variantIndex];
const stokSaatIni = variant.stock;
// 2. Validasi stok sebelum dipotong
if (stokSaatIni < qtyBeli) {
throw new Error(`Stok tidak mencukupi! Tersedia: ${stokSaatIni}, Diminta: ${qtyBeli}`);
}
// 3. Kalkulasi data baru
const updatedVariants = [...productData.variants];
updatedVariants[variantIndex] = {
...variant,
stock: stokSaatIni - qtyBeli
};
// 4. Tulis perubahan di dalam transaksi (Write)
transaction.update(productRef, {
variants: updatedVariants,
updatedAt: new Date().toISOString()
});
return { success: true, sisaStok: stokSaatIni - qtyBeli };
});
console.log("Transaksi Berhasil!", hasil);
} catch (error) {
console.error("Transaksi Gagal / Conflict:", error.message);
}
}3. Autentikasi Pengguna & OTP Tanpa Sandi (SDK v2.0.0)
Inisialisasi Auth & Realtime State Listener
import { getAuth, NexaAuthError } from 'nexabase-console';
const auth = getAuth(app);
// Realtime Listener & Sync Sesi Multi-Tab via StorageEvent
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
console.log("User terautentikasi:", user.uid, "| Email:", user.email);
} else {
console.log("User ter-logout / belum login");
}
});Pendaftaran & Login Email/Password
// 1. Registrasi Akun Baru
const register = async () => {
try {
const session = await auth.createUserWithEmailAndPassword(
'[email protected]',
'Password123!',
'Budi Santoso'
);
console.log("Registrasi Berhasil! UID:", session.user.uid);
} catch (err) {
if (err instanceof NexaAuthError && err.code === 'auth/email-already-in-use') {
console.error("Email sudah terdaftar.");
}
}
};
// 2. Login User (Dukungan Automatic Single-Flight Token Refresh)
const login = async () => {
try {
const session = await auth.signInWithEmailAndPassword(
'[email protected]',
'Password123!'
);
console.log("Access Token JWT:", session.accessToken);
console.log("Refresh Token:", session.refreshToken);
} catch (err) {
if (err instanceof NexaAuthError && err.code === 'auth/invalid-credential') {
console.error("Email atau password salah.");
}
}
};Login Passwordless dengan OTP (Email 6-Digit)
// 1. Kirim OTP Ke Email (Berlaku 5 Menit, Hash Single-Use)
const sendMyOtp = async () => {
const res = await auth.sendOtp('[email protected]');
console.log("Kode OTP berhasil terkirim:", res.message);
};
// 2. Verifikasi OTP dari Input Pengguna
const verifyMyOtp = async () => {
try {
const otpCode = '123456'; // Kode 6 digit yang dikirim ke email
const session = await auth.signInWithOtp('[email protected]', otpCode);
console.log("Login OTP Berhasil! Welcome:", session.user.email);
} catch (err) {
if (err instanceof NexaAuthError) {
if (err.code === 'auth/invalid-otp') console.error("OTP tidak valid.");
if (err.code === 'auth/expired-otp') console.error("OTP kedaluwarsa.");
if (err.code === 'auth/too-many-otp-attempts') console.error("Terlalu banyak percobaan.");
}
}
};Mengambil ID Token untuk Request API / Server Verification
// Mengambil token JWT (dengan auto-refresh jika hampir expired)
const idToken = await auth.getIdToken(/* forceRefresh */ false);
// Mengambil rincian klaim token (exp, authTime, claims)
const tokenResult = await auth.getIdTokenResult();
console.log("Token Kedaluwarsa:", tokenResult.expirationTime);Update Profil, Reset Password & Sign Out
// Update Profil
await auth.updateProfile({ name: 'Budi Santoso, M.T.', photoURL: 'https://example.com/photo.jpg' });
// Reset Password Email
await auth.sendPasswordResetEmail('[email protected]');
// Revoke All Sessions
await auth.revokeAllSessions();
// === Opsi Logout Akun (Sign Out) ===
// 1. Method Instance (Rekomendasi)
await auth.signOut();
// 2. Fungsi Standar Modular
import { signOut } from 'nexabase-console';
await signOut(auth);
// 3. Namespace Helper
import { NexabaseAuth } from 'nexabase-console';
await NexabaseAuth.signOut(auth);
// 4. Shortcut dari objek App
await app.signOut();4. File Storage API
const uploadImage = async (fileBlob) => {
const storageRef = app.storage().ref('avatars/andi.jpg');
const result = await storageRef.put(fileBlob);
console.log("Akses File URL Publik Anda:", result.url);
};📂 Penanganan Tipe Konten Dinamis (Form Data Boundary)
Unggahan file menggunakan format multipart standard yang dibungkus dalam objek FormData. SDK ini secara otomatis mendeteksi payload FormData dan membebaskan header Content-Type bawaan agar browser dapat secara dinamis menetapkan tipe konten yang menyertakan kode batas unik (multipart boundary).
Ini menghilangkan bug berkas kosong ({}) yang disebabkan oleh tumpang tindihnya header application/json di Axios.
5. Keamanan Tipe TypeScript (Generics) & Kueri Lanjutan
Dukungan TypeScript Generics
Kini Anda bisa mendefinisikan tipe data struktur dokumen Anda agar penulisan dan pembacaan data sepenuhnya aman (Type-safe).
import { collection, doc, getDoc, getDocs } from 'nexabase-console';
interface Product {
name: string;
price: number;
stock: number;
tags: string[];
}
// 1. Definisikan tipe koleksi
const productsCol = collection<Product>(db, 'products');
// 2. Definisikan tipe referensi dokumen
const productRef = doc<Product>(db, 'products', 'prod_a');
// 3. Baca data dengan Autocomplete & Type Checking penuh
const snapshot = await getDoc(productRef);
if (snapshot.exists()) {
const data = snapshot.data(); // Tipe data otomatis terdeteksi sebagai 'Product'
console.log(data.name); // Aman! Autocomplete tersedia
console.log(data.price); // Aman!
}Kompatibilitas QuerySnapshot (docChanges())
Bagi Anda yang bermigrasi dari Firebase, objek QuerySnapshot yang dikembalikan oleh getDocs atau onSnapshot kini mendukung metode .docChanges() secara native:
const snapshot = await getDocs(productsCol);
const changes = snapshot.docChanges(); // Mengembalikan array perubahan dokumenFilter Offline Lengkap (Advanced Querying Operators)
Mekanisme kueri offline pada SDK NexaBase kini mendukung operator Firestore tingkat lanjut secara penuh demi keandalan luar biasa bahkan saat koneksi terputus:
- Operator dasar:
==,!=,>,<,>=,<= - Operator array & keanggotaan:
in,not-in,array-contains,array-contains-any
🗄️ Dukungan Sinkronisasi Offline (IndexedDB)
NexaBase JS SDK dikembangkan dengan arsitektur Offline-First.
- Optimistic Rendering: Jika perangkat offline, SDK akan langsung mengupdate cache IndexedDB lokal dan men-trigger callback subscriber
onSnapshotagar UI pengguna langsung merespons secara instan. - Background Queue: Setiap perubahan mutasi (
setDoc,updateDoc,deleteDoc,writeBatch) saat offline disimpan secara aman di IndexedDB. - Automatic Resync: Saat koneksi internet mendeteksi status
online, antrean modifikasi akan dikirimkan kembali secara beruntun ke Server cloud NexaBase secara otomatis tanpa intervensi pengguna.
📄 Lisensi
Proyek ini dilisensikan di bawah Lisensi MIT. Bebas digunakan untuk keperluan pribadi, komersial, maupun edukasi.
