Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/n1md7/indexeddb-promise

Indexeddb wrapper with promises
https://github.com/n1md7/indexeddb-promise

database indexed-db indexed-db-orm indexeddb-wrapper javascript typescript

Last synced: 26 days ago
JSON representation

Indexeddb wrapper with promises

Awesome Lists containing this project

README

        

[![npm databaseVersion](https://badge.fury.io/js/@n1md7%2Findexeddb-promise.svg)](https://badge.fury.io/js/@n1md7%2Findexeddb-promise)
[![Node.js Package](https://github.com/n1md7/indexeddb-promise/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/n1md7/indexeddb-promise/actions/workflows/npm-publish.yml)
[![Node.js CI - tests](https://github.com/n1md7/indexeddb-promise/actions/workflows/node.js.yml/badge.svg)](https://github.com/n1md7/indexeddb-promise/actions/workflows/node.js.yml)
![GitHub](https://img.shields.io/github/license/n1md7/indexeddb-promise)
[![codecov](https://codecov.io/gh/n1md7/indexeddb-promise/branch/master/graph/badge.svg?token=Q5OJ22Q3LK)](https://codecov.io/gh/n1md7/indexeddb-promise)

# Indexed DB wrapper with promises

Supports all browsers that support IndexedDB.

Has TypeScript support with TS-class decorators.

## Demo

- [Basic Example01](https://n1md7.github.io/indexeddb-promise/Example01.html)
- [Advanced Example02](https://n1md7.github.io/indexeddb-promise/Example02.html)

## Installation

```shell
$ npm install @n1md7/indexeddb-promise
```

```shell
$ yarn add @n1md7/indexeddb-promise
```

## Decorators

Typescript decorators are supported.

Prerequisite:

```json
{
"experimentalDecorators": true
}
```

### @Table

Class decorator for IndexedDB table. It defines the structure of the table.

Params: options - object with following properties:

- name? - string, name of the table, default is the class name
- timestamps? - boolean, if true, createdAt and updatedAt columns will be added, default is false
- initialData? - array of objects to be inserted into the table on database creation, default is empty

```javascript
@Table({ name: 'Users', timestamps: true })
class User {}
```

### @Indexed

Property decorator for IndexedDB index. It defines whether the property is indexed.

Params: options - object with following properties:

- unique? - boolean, if true, the index will be unique, default is false
- multiEntry? - boolean, if true, the index will be multi-entry, default is true

```javascript
@Table()
class User {
@Indexed({ unique: true, multiEntry: false })
username: string;
}
```

### @PrimaryKey

Property decorator for IndexedDB primary key. It defines whether the property is primary key.

Params: options - object with following properties:

- autoIncrement? - boolean, if true, the primary key will be auto-incremented, default is true
- unique? - boolean, if true, the primary key will be unique, default is true

```javascript
@Table()
class User {
@PrimaryKey()
id: number;

@Index()
name: string;
}
```

## Methods

| Method | Description | Params |
| ------------------ | ------------------------------------------------- | ----------------------- |
| `selectAll` | Get the data from database | None |
| `select` | Fetch data from database with filters | Object |
| `insert` | Add data into database | Object |
| `openCursor` | Get database cursor to iterate on demand | None |
| `selectByIndex` | Select database data by indexed properties (one) | String, String |
| `selectByIndexAll` | Select database data by indexed properties (many) | String, String |
| `selectByPk` | Select database record by primary key | String \ Number |
| `updateByPk` | Update database record by primary key | Number \ Number, Object |
| `deleteByPk` | Delete database record by primary key | String \ Number |

### - selectAll

#### Params

Gets all the data from database.

Return type: Promise

Accept params: None

#### Javascript example

```javascript
model.selectAll().then((data) => data);
const data = await model.selectAll();
```

#### Typescript example

```typescript
model.selectAll().then((data) => {
// In TS {data} is inferred from the model
return data;
});
const data = await model.selectAll();
```

### - select

#### Params

Gets filtered data from database.

Return type: Promise

Accept params: options: Object

Object properties:

```text
{
where?: Object | Function;
limit?: Number;
orderByDESC?: Boolean;
sortBy?: String | String[];
}
```

#### JS/TS example

```javascript
const options = {
limit: 10,
where: (dataArray) => {
return dataArray;
},
orderByDESC: true,
sortBy: 'comments', // ['comments', 'date']
};
model.select(options).then((data) => data);
const data = await model.select(options);
```

@where property can filter out data like

```javascript
const props = {
where: (data) => data.filter((item) => item.username === 'admin'),
};
```

or it can be an object, which gets data with AND(&&) comparison

```javascript
const props = {
where: {
username: 'admin',
password: 'admin123',
},
};
```

### - selectByIndex

#### Params

Gets the data from database with the specified indexed property.

Return type: Promise

Accept params: indexName: string, valueToMatch: string

#### JS/TS example

```javascript
model.selectByIndex('username', 'admin').then((data) => data);
const data = await model.selectByIndex('username', 'admin');
```

### - selectByIndexAll

#### Params

Gets the data from database with the specified indexed property.

Return type: Promise>

Accept params: indexName: string, valueToMatch: string

#### JS/TS example

```javascript
model.selectByIndexAll('username', 'admin').then((data) => data);
const data = await model.selectByIndexAll('username', 'admin');
```

### - selectByPk

#### Params

Gets the data from database with the specified primary key.

Return type: Promise

Accept params: primaryKey: string \ number

#### JS/TS example

```javascript
model.selectByPk(1).then((data) => data);
const data = await model.selectByPk('admin');
```

### - updateByPk

#### Params

Updates the data in database with the specified primary key.

Return type: Promise

Accept params: primaryKey: string \ number, Object

#### JS/TS example

```javascript
model.updateByPk(123, { username: 'admin' });
const data = await model.updateByPk(123, { username: 'admin' });
```

### - deleteByPk

#### Params

Deletes the data in database with the specified primary key.

Return type: Promise

Accept params: primaryKey: string \ number

#### JS/TS example

```javascript
model.deleteByPk(123);
const data = await model.deleteByPk(123);
```

_note_ Primary key is type sensitive. If it is saved as integer then should pass as integer and vice versa

## Examples

### Html 01

Once you add _indexed-db.min.js_ in your document then you will be able to access
`IndexedDB` variable globally which contains `Model`. They can be extracted as following

```html


IndexedDB app




const Database = IndexedDB.Database;
// Your script here

```

### Html 02

Basic usage of `Model`

```html


IndexedDB app




const db = new IndexedDB.Database({
databaseVersion: 1,
databaseName: 'MyNewDatabase',
tables: [
{
name: 'MyNewTable',
primaryKey: {
name: 'id',
autoIncrement: false,
unique: true,
},
initData: [],
indexes: {
username: { unique: false, autoIncrement: false },
password: { unique: false, autoIncrement: false },
},
},
],
});
await db.connect();
// Add a new record
const model = db.useModel('MyNewTable');
model
.insert({
id: Math.random() * 10,
username: 'admin',
password: 'nimda',
createdAt: new Date(),
updatedAt: new Date(),
})
.then(function() {
console.info('Yay, you have saved the data.');
})
.catch(function(error) {
console.error(error);
});

// Get all results from the database
model.selectAll().then(function(results) {
console.log(...results);
});

```

### Javascript

```javascript
const IndexedDB = require('@n1md7/indexeddb-promise');
const { Database } = IndexedDB;
// or
import { Database } from '@n1md7/indexeddb-promise';
```

### TypeScript 01

```javascript
import { Database } from '@n1md7/indexeddb-promise';

interface Users {
id?: number;
username: string;
password: string;
}

enum Priority {
LOW = 'LOW',
MEDIUM = 'MEDIUM',
HIGH = 'HIGH',
}

interface ToDos {
id?: number;
userId: number;
title: string;
description: string;
done: boolean;
priority: Priority;
}

const database = new Database({
version: 1,
name: 'Todo-list',
tables: [
{
name: 'users',
primaryKey: {
name: 'id',
autoIncrement: true,
unique: true,
},
indexes: {
username: {
unique: false,
},
},
timestamps: true,
},
{
name: 'todos',
primaryKey: {
name: 'id',
autoIncrement: true,
unique: true,
},
indexes: {
userId: {
unique: true,
},
},
timestamps: true,
},
],
});

(async () => {
await database.connect();
const users = database.useModel('users');
const user = await users.insert({
username: 'admin',
password: 'admin',
});
const todos = database.useModel('todos');
await todos.insert({
userId: user.id,
title: 'Todo 1',
description: 'Description 1',
done: false,
priority: Priority.LOW,
});
})();
```

### TypeScript decorators 02

```javascript
import { Table, PrimaryKey, Indexed, Database } from '@n1md7/indexeddb-promise';

@Table({ name: '__Name__', timestamps: true })
class SomeTable {
@PrimaryKey({ autoIncrement: true, unique: true })
id: number;

@Indexed({ unique: true, multiEntry: false })
username: string;

@Indexed({ unique: false })
age: number;

otherData: string;
}

const anotherDb = new Database({
version: 1,
name: 'Other-DB',
tables: [SomeTable],
});
await anotherDb.connect();
const model = anotherDb.useModel(SomeTable);

(async () => {
await model
.insert({
username: 'John',
age: 20,
otherData: 'Some data',
})
.catch((error) => console.error(error));

model.selectAll().then((results) => {
if (results) {
results.forEach((result) => {
// result is inferred to be SomeTable
console.log(result.username);
});
}
});
})();
```

### TypeScript decorators 02

```javascript
import { Database, Indexed, PrimaryKey, Table } from '@n1md7/indexeddb-promise';

@Table({ timestamps: true })
class User {
@PrimaryKey({ autoIncrement: true, unique: true })
id: number;

@Indexed({ unique: true, multiEntry: false })
username: string;

@Indexed()
password: string;

toString() {
return `#${this.id} ${this.username}`;
}
}

@Table()
class Info {
@PrimaryKey({ autoIncrement: true, unique: true })
id: number;

@Indexed()
userId: number;

name: {
first: string,
last: string,
};

age: number;

occupation: string;

toString() {
return `${this.name.first} ${this.name.last} ${this.age} years old - ${this.occupation}`;
}
}

const store = new Database({
version: 1,
name: 'Store',
tables: [User, Info],
});

await store.connect();

const userModel = store.useModel(User);
const infoModel = store.useModel(Info);

(async () => {
const savedUser = await userModel.insert({
username: 'admin',
password: 'admin',
});

await infoModel.insert({
userId: savedUser.id,
name: {
first: 'John',
last: 'Doe',
},
age: 27,
occupation: 'Web Developer',
});
})().catch(console.log);

(async () => {
const user = await userModel.selectByIndex('username', 'admin');
if (!user) throw new Error('User not found');

const info = await infoModel.select({ where: { userId: user.id } });
if (!info) throw new Error('Info not found');

console.log(user.toString() + ' - ' + info.toString());
})().catch(console.log);
```