Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mockingmagician/promised-db
Promised version of indexed DB that simplify usage
https://github.com/mockingmagician/promised-db
Last synced: 5 days ago
JSON representation
Promised version of indexed DB that simplify usage
- Host: GitHub
- URL: https://github.com/mockingmagician/promised-db
- Owner: MockingMagician
- License: isc
- Created: 2024-06-16T21:42:58.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2024-09-18T07:13:17.000Z (about 2 months ago)
- Last Synced: 2024-09-18T09:42:43.124Z (about 2 months ago)
- Language: TypeScript
- Size: 5.14 MB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Funding: FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# Overview
**@idxdb/promised** is a lightweight library that wraps the IndexedDB API, providing a more natural way to work with promises. It allows you to easily store and retrieve data in an indexed database using async/await syntax, making it easier to integrate with your existing codebase.
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
[![NPM Version](https://img.shields.io/npm/v/@idxdb/promised)](https://www.npmjs.com/package/@idxdb/promised)
[![dependencies](https://img.shields.io/badge/dependencies-free-white.svg)](https://shields.io/)
[![Known Vulnerabilities](https://snyk.io/test/github/MockingMagician/promised-db/badge.svg)](https://snyk.io/test/github/MockingMagician/promised-db)
[![test](https://github.com/MockingMagician/promised-db/actions/workflows/test-and-deploy.yaml/badge.svg)](https://github.com/MockingMagician/promised-db/actions/workflows/test-and-deploy.yaml)
[![codecov](https://codecov.io/gh/MockingMagician/promised-db/branch/main/graph/badge.svg)](https://codecov.io/gh/MockingMagician/promised-db)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/e840ba7190714112b4a7ad6bd54fed68)](https://app.codacy.com/gh/MockingMagician/promised-db/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)## Dependencies
This package has no dependencies.
## Table of Contents
- [Installation](#installation)
- [With npm](#with-npm)
- [With yarn](#with-yarn)
- [ESM Module](#esm-module)
- [Usage in the Browser](#usage-in-the-browser)
- [Foreword](#foreword)
- [Usage - API](#usage---api)
- [Database initialization and migration management](#database-initialization-and-migration-management)
- [What if I don't have any migrations?](#what-if-i-dont-have-any-migrations)
- [What happens if my DB is already open and I try to open another version?](#what-happens-if-my-db-is-already-open-and-i-try-to-open-another-version)
- [Add some data](#add-some-data)
- [Fetch some data](#fetch-some-data)
- [Iterate over cursor](#iterate-over-cursor)
- [And all the existing API](#and-all-the-existing-api)
- [⚠️ WARNING: Asynchronous Context and IndexedDB Transactions](#️-warning-asynchronous-context-and-indexeddb-transactions)
- [Contributing](#contributing)
- [License](#license)
- [Contact](#contact)
- [Versioning](#versioning)## Installation
To install this package, run the following command in your terminal:
- With npm
```
npm install @idxdb/promised
```- With yarn
```
yarn add @idxdb/promised
```### ESM Module
**@idxdb/promised** is an ECMAScript (ESM) module, which means it can be directly imported in environments that support modules, such as modern browsers or Node.js with ESM enabled.
#### Usage in the Browser
You can load the module directly from a CDN using the following code:
```html
```
Once loaded, a DatabaseFactory object will be available in the global scope, allowing you to interact with the **@idxdb/promised** API directly.
This allows you to use **@idxdb/promised** without the need to download it locally or set up a bundler like Webpack or Rollup.
## Foreword
This package fully respects the original indexedDB API.
The only subtleties are:
- database initialization, to support migrations during version upgrades.
- in the cursors, which allow more natural iteration than the original API.
Detailed interfaces
```typescript
export interface DatabaseInterface {
close(): void
createObjectStore(
name: string,
options?: IDBObjectStoreParameters
): ObjectStoreInterface
deleteObjectStore(name: string): void
transaction(
storeNames: string | string[],
mode?: IDBTransactionMode,
options?: IDBTransactionOptions
): TransactionInterface
objectStoreNames: string[]
}export interface ObjectStoreInterface {
add(value: V, key?: K): Promise
clear(): Promise
count(query?: IDBKeyRange | K): Promise
createIndex(
indexName: string,
keyPath: string | string[],
options?: IDBIndexParameters
): IndexInterface
delete(query: IDBKeyRange | K): Promise
deleteIndex(name: string): void
get(key: K): Promise
getAll(
query?: IDBKeyRange | K,
count?: number
): Promise
getAllKeys(
query?: IDBKeyRange | K,
count?: number
): Promise
getKey(key: IDBKeyRange | K): Promise
index(name: string): IndexInterface
openCursor(
query?: IDBKeyRange | K,
direction?: IDBCursorDirection
): ValueCursorInterface
openKeyCursor(
query?: IDBKeyRange | K,
direction?: IDBCursorDirection
): KeyCursorInterface
put(value: V, key?: K): Promise
indexNames: string[]
}export interface IndexInterface {
keyPath: string | string[]
multiEntry: boolean
name: string
objectStore: ObjectStoreInterface
unique: boolean
count(query?: IDBKeyRange | K): Promise
get(key: K): Promise
getAll(
query?: IDBKeyRange | K,
count?: number
): Promise
getAllKeys(
query?: IDBKeyRange | K,
count?: number
): Promise
getKey(key: IDBKeyRange | K): Promise
openCursor(
query?: IDBKeyRange | K,
direction?: IDBCursorDirection
): ValueCursorInterface
openKeyCursor(
query?: IDBKeyRange | K,
direction?: IDBCursorDirection
): KeyCursorInterface
}export interface ValueCursorInterface<
PK extends IDBValidKey,
K extends IDBValidKey,
R,
> extends KeyCursorInterface {
value: R | undefined
delete(): Promise
update(value: R): Promise
}export interface KeyCursorInterface<
PK extends IDBValidKey,
K extends IDBValidKey,
> {
primaryKey: PK | undefined
key: K | undefined
direction: IDBCursorDirection
source: ObjectStoreInterface | IndexInterface
request: IDBRequest
end(): Promise
continue(key?: K): void
advance(count: number): void
continuePrimaryKey(key: K, primaryKey: PK): void
}export interface TransactionInterface {
abort(): Promise
commit(): Promise
objectStore(name: string): ObjectStoreInterface
objectStoreNames: string[]
db: DatabaseInterface
durability: IDBTransactionDurability
error: DOMException
mode: IDBTransactionMode
}
```## Usage - API
### Database initialization and migration management
```typescript
import { DatabaseFactory } from '@idxdb/promised';const migrations = [
{
version: 1,
migration: async ({db, transaction, dbOldVersion, dbNewVersion, migrationVersion}) => {
const store = db.createObjectStore('users', { keyPath: 'id' })
store.createIndex('name_idx', 'name', { unique: false });
},
},
{
version: 2,
migration: async ({db, transaction, dbOldVersion, dbNewVersion, migrationVersion}) => {
const store = transaction.objectStore('users')
store.createIndex('email_idx', 'email', { unique: true })
},
},
{
version: 3,
migration: async ({db, transaction, dbOldVersion, dbNewVersion, migrationVersion}) => {
const store = transaction.objectStore('users')
store.createIndex('identifier_idx', 'identifier', { unique: true })
store.deleteIndex('email_idx')
},
},
]const requestedVersion = 3;
const db = await DatabaseFactory.open('mydatabase', requestedVersion, migrations);
```#### What if I don't have any migrations to keep up to date and I just delete my DB every time I change?
All you have to do is keep one migration and you're done.
```typescript
import { DatabaseFactory } from '@idxdb/promised';const requestedVersion = 3; // the version you want to open, increase to open anew one and reset your DB scheme
const migrations = [
{
version: requestedVersion,
migration: async ({db, transaction, dbOldVersion, dbNewVersion, migrationVersion}) => {
for (const store of db.objectStoreNames) {
db.deleteObjectStore(store) // correctly delete all previous existing stores
}
// creates the fresh new stores and their indexes
const store = db.createObjectStore('users', { keyPath: 'id' })
store.createIndex('name_idx', 'name', { unique: false });
//...
},
},
]const db = await DatabaseFactory.open('mydatabase', requestedVersion, migrations);
```#### What happens if my DB is already open and I try to open another version?
A basic error message will inform you that the operation is impossible. However, you can take control by adding a handler for the `blocked` event and perform the actions you deem necessary:
- Warn the user
- Inform other tabs
- ...```typescript
import { DatabaseFactory } from '@idxdb/promised';let db = await DatabaseFactory.open(dbName, 1)
const bc = new BroadcastChannel('idxdb_channel')
bc.onmessage = (event) => {
if (event.data.action === 'close-db') {
db.close();
}
}const onBlocked = async ({ oldVersion, newVersion }) => {
// warn other tabs to close the db
bc.postMessage({ action: 'close-db' });
return 'close-db-emitted'
}db = await DatabaseFactory.open(dbName, 2, [], onBlocked).catch((error) => {
if (error === 'close-db-emitted') {
// retry the open
return DatabaseFactory.open(dbName, 2)
}
})
```### Add some data
```typescript
import { DatabaseFactory } from '@idxdb/promised';const db = await DatabaseFactory.open('mydatabase', 1);
const tx = db.transaction(['users'], "readwrite");
const store = tx.objectStore('users');store.add({ id: 1, name: 'Jane Doe' });
await tx.commit();
```### Fetch some data
```typescript
import { DatabaseFactory } from '@idxdb/promised';const db = await DatabaseFactory.open('mydatabase', 1);
const tx = db.transaction(['users'], "readonly");
const store = tx.objectStore('users');const result = await store.get(1);
console.log(result.name); // "John Doe"
```### Iterate over cursor
```typescript
import { DatabaseFactory } from '@idxdb/promised';const db = await DatabaseFactory.open('mydatabase', 1);
const tx = db.transaction(['users'], "readonly");
const store = tx.objectStore('users');
const cursor = store.openCursor();while (!(await cursor.end())) {
console.log(cursor.value);
cursor.continue();
}
```### And all the existing API
The library implements all the methods of the IndexedDB API, you can find the documentation [here](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API).
You can also find more examples in the [tests](https://github.com/MockingMagician/promised-db/tree/main/test/unit/component)
## ⚠️ WARNING: Asynchronous Context and IndexedDB Transactions
When working with IndexedDB transactions, it is critical to be aware of how asynchronous functions interact with the event loop. If you initiate a transaction and then call any function that triggers the event loop (such as `setTimeout`, `fetch`, or similar asynchronous operations), IndexedDB will automatically close the ongoing transaction. This behavior can lead to unexpected errors, particularly if the transaction is closed before all operations are completed.
For example:
```typescript
import { DatabaseFactory } from '@idxdb/promised';const db = await DatabaseFactory.open('mydatabase', 1);
const tx = db.transaction(['users'], 'readwrite');
const store = tx.objectStore('users');// This may result in an error if fetch is called within the transaction
fetch('/api/data').then((response) => {
return response.json();
}).then((data) => {
store.add(data);
});// The transaction may be automatically closed by the event loop before the store operation completes
await tx.commit(); // Error: Transaction closed
```## Contributing
If you'd like to contribute to this package, please follow these steps:
- Fork this repository
- Make your changes and commit them
- Create a pull request with a detailed description of the changes## License
This package is licensed under ISC. See the LICENSE file for more information.
## Contact
If you have any questions or need further assistance, please feel free to reach out to me at [Marc MOREAU](mailto:[email protected]).
## Versioning
This package uses [SemVer](https://semver.org/) for versioning. For the versions available, see the tags on this repository.