How to use IndexedDB
Here are some of the more important IndexedDB functionalities, wrapped in a simple Promise API.
export function openDatabase(
dbName: string,
version: number,
init: (db: IDBDatabase) => void
): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
if (import.meta.client) {
const request = indexedDB.open(dbName, version);
request.onupgradeneeded = () => {
const db = request.result;
init(db);
};
request.onsuccess = () => {
resolve(request.result);
};
request.onerror = () => {
reject(request.error);
};
} else {
reject(new Error("IndexedDB code does not run on server"));
}
});
}
export function saveRecord(
db: IDBDatabase,
store: string,
record: any
): Promise<IDBValidKey> {
return new Promise((resolve, reject) => {
if (import.meta.client) {
const transaction = db.transaction(store, "readwrite");
const objectStore = transaction.objectStore(store);
const request = objectStore.put(record);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
} else {
reject(new Error("IndexedDB code does not run on server"));
}
});
}
export function fetchAllRecords(
db: IDBDatabase,
store: string
): Promise<any[]> {
return new Promise((resolve, reject) => {
if (import.meta.client) {
const transaction = db.transaction(store, "readwrite");
const objectStore = transaction.objectStore(store);
const request = objectStore.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
} else {
reject(new Error("IndexedDB code does not run on server"));
}
});
}
export function fetchRecordById(
db: IDBDatabase,
store: string,
id: IDBValidKey
): Promise<any> {
return new Promise((resolve, reject) => {
if (import.meta.client) {
const transaction = db.transaction(store, "readwrite");
const objectStore = transaction.objectStore(store);
const request = objectStore.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
} else {
reject(new Error("IndexedDB code does not run on server"));
}
});
}
export function deleteRecord(db: IDBDatabase, store: string, id: IDBValidKey) {
return new Promise((resolve, reject) => {
if (import.meta.client) {
const transaction = db.transaction(store, "readwrite");
const objectStore = transaction.objectStore(store);
const request = objectStore.delete(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
} else {
reject(new Error("IndexedDB code does not run on server"));
}
});
}
And here is the same API wrapped in an object-oriented class:
export type DatabaseConfig = {
name: string;
version: number;
stores?: { name: string; keyPath: string }[];
init?: (db: IDBDatabase) => void;
};
class ObjectStore {
#db: IDBDatabase;
name: string;
constructor(db: IDBDatabase, name: string) {
this.#db = db;
this.name = name;
}
save(record: any) {
return saveRecord(this.#db, this.name, record);
}
fetch() {
return fetchAllRecords(this.#db, this.name);
}
delete(id: IDBValidKey) {
return deleteRecord(this.#db, this.name, id);
}
}
// dummy for server
export class Database {
#db?: IDBDatabase = undefined;
onopen?: Function;
constructor({ name, version, stores, init }: DatabaseConfig) {
if (import.meta.client) {
openDatabase(name, version, (db) => {
if (stores) {
for (const store of stores) {
db.createObjectStore(store.name, {
keyPath: store.keyPath,
});
}
}
if (init) {
init(db);
}
})
.then((result) => (this.#db = result))
.then(() => {
if (this.onopen) {
this.onopen();
}
});
}
}
save(store: string, record: any) {
if (this.#db) {
return saveRecord(this.#db, store, record);
} else {
throw new Error("database not initialized correctly");
}
}
fetch(store: string) {
if (this.#db) {
return fetchAllRecords(this.#db, store);
} else {
throw new Error("database not initialized correctly");
}
}
delete(store: string, id: IDBValidKey) {
if (this.#db) {
return deleteRecord(this.#db, store, id);
} else {
throw new Error("database not initialized correctly");
}
}
getStore(store: string) {
if (this.#db) {
return new ObjectStore(this.#db, store);
} else {
throw new Error("database not initialized correctly");
}
}
}