@pvermeer/dexie-populate-addon
TypeScript icon, indicating that this package has built-in type declarations

1.0.5 • Public • Published

Dexie Populate Addon

NPM Version NPM Version master lerna Conventional Commits

This addon can be used as a stand-alone addon for Dexie.js yet is also part of dexie-addon-suite NPM Version that combines a number of addons for Dexie. It contains code to combine some addons like populated rxjs observables.

Install over npm

npm install @pvermeer/dexie-populate-addon

Dexie.js

Dexie Populate Addon depends on Dexie.js v3. NPM Version

npm install dexie

Documentation

Populate your data the Dexie way!

Addon is written to be as easy to use as Dexie.js itself.

Added Schema Syntax

Symbol Description
=> Relational notation: group => groups.id (group is indexed)

Population

Index keys of group may be an array of keys [1, 2, 3] or a single key 1.

Indices

Relational keys will be indexed. Multi-index *groups and compound indices can be used ++id, group => groups.id, [id+group].

How to use

Addon

Add populate() to your Dexie database. See below examples and https://dexie.org for more info.

Ref type (TypeScript)

For typescript there is a special Ref type for your Classes and Interfaces to let the type system know that this is a potentially populated property:

import { Ref } from "@pvermeer/dexie-populate-addon";

class Club {
  id?: number;
  name: string;
  size: number;
  description: string;
}

export class Friend {
  id?: number;
  firstName: string;
  lastName: string;
  memberOf: Ref<Club, number>[];

  doSomething() {}
}

With this notation we let the typesystem know we have a property memberOf that can be assigned with index keys of number. When population methods are used, TypeScript now knows that this has changed to Club[] in memberOf. If a Ref is not found it is null, thus the result for memberOf will be (Club | null)[].

The Ref type is a (fake) nominal type so the type system can differentiate this type from other assignable types.

Populate method

The Table class of Dexie is extended with the populate() method:

interface PopulateOptions {
    shallow: boolean;
}

populate(keysOrOptions?: string[] | PopulateOptions): PopulateTable;

It returns a new PopulateTable with the available Dexie.Table methods that support population. Dexie Table methods can then be used as normal.

When no options are provided, the return is a deep populated record. Since this is not always wanted and to speed up the database lookup, options can be provided:

  • Providing string[] expects population keys as provided in the table schema. Only thoses properties will be populated .
  • Providing { shallow: true } disables deep population. Only one layer of population is applied.
Examples:

Options:

db.friends.populate().get(1); // Fully populated;
db.friends.populate({ shallow: true }).get(1); // Only the record itself is populated, no deeper;
db.friends.populate(["memberOf", "group"]).get(1); // Only 'memberOf' and 'group' are deep populated;
db.friends.populate(["memberOf", "group"], { shallow: true }).get(1); // Only 'memberOf' and 'group' are shallow populated;

Array methods:

db.friends.populate().where(":id").equals(1).first();
db.friends.populate().toArray();

Compound:

db.friends.populate().where("[id+group]").equals([1, 2]).first();
db.friends.populate().where({ id: 1, group: 2 }).first();

And more... see https://dexie.org

Create Dexie database

ESM

import Dexie from "dexie";
import { populate } from "@pvermeer/dexie-populate-addon";

// Declare Database
const db = new Dexie("FriendDatabase", {
  addons: [populate],
});
db.version(1).stores({
  friends: "++id, name, *memberOf => clubs.id, group => groups.id, [id+group]",
  clubs: "++id, name, theme => themes.id",
  themes: "++id, name, style => styles.styleId",
  styles: "++styleId, name, color",
  groups: "++id, name",
});

// Open the database
db.open().then(() => {
  console.log("DB loaded! :D");
  // Use Dexie
});

Typescript

import Dexie from "dexie";
import { populate, Ref } from "@pvermeer/dexie-populate-addon";

// Declare classes
class Style {
  styleId?: number;
  name: string;
  color: string;
  description: string;
}
class Theme {
  id?: number;
  name: string;
  style: Ref<Style, number>;
  description: string;
}
class Club {
  id?: number;
  name: string;
  size: number;
  theme: Ref<Theme, number>;
  description: string;
}
class Group {
  id?: number;
  name: string;
  true: boolean;
  description: string;
}
class Friend {
  id?: number;
  name: string;
  memberOf: Ref<Club, number>[];
  group: Ref<Group, number>;

  doSomething() {
    return "done";
  }
}

// Declare Database
class FriendsDatabase extends Dexie {
  public friends: Dexie.Table<Friend, number>;
  public clubs: Dexie.Table<Club, number>;
  public themes: Dexie.Table<Theme, number>;
  public groups: Dexie.Table<Group, number>;
  public styles: Dexie.Table<Style, number>;

  constructor(name: string) {
    super(name);

    populate(this);

    this.version(1).stores({
      friends:
        "++id, name, *memberOf => clubs.id, group => groups.id, [id+group]",
      clubs: "++id, name, theme => themes.id",
      themes: "++id, name, style => styles.styleId",
      styles: "++styleId, name, color",
      groups: "++id, name",
    });

    this.friends.mapToClass(Friend);
    this.clubs.mapToClass(Club);
    this.themes.mapToClass(Theme);
    this.groups.mapToClass(Group);
    this.styles.mapToClass(Style);
  }
}

const db = new FriendDatabase("FriendsDatabase");

// Open the database
db.open().then(() => {
  console.log("DB loaded! :D");
  // Use Dexie
});

HTML import

Bundled & minified package: https://unpkg.com/@pvermeer/dexie-populate-addon@latest/dist/dexie-populate-addon.min.js.

Addon is export as namespace DexiePopulateAddon

<!DOCTYPE html>
<html>
  <head>
    <!-- Include Dexie (@next if v3 is still in RC) -->
    <script src="https://unpkg.com/dexie@latest/dist/dexie.js"></script>

    <!-- Include DexiePopulateAddon (always after Dexie, it's a dependency) -->
    <script src="https://unpkg.com/@pvermeer/dexie-populate-addon@latest/dist/dexie-populate-addon.min.js"></script>

    <script>
      // Define your database
      const db = new Dexie("FriendDatabase", {
        addons: [DexiePopulateAddon.populate],
      });
      db.version(1).stores({
        friends:
          "++id, name, *memberOf => clubs.id, group => groups.id, [id+group]",
        clubs: "++id, name, theme => themes.id",
        themes: "++id, name, style => styles.styleId",
        styles: "++styleId, name, color",
        groups: "++id, name",
      });

      // Open the database
      db.open().then(() => {
        console.log("DB loaded! :D");
        // Do Dexie stuff
      });
    </script>
  </head>
</html>

API

populate - addon function

function populate(db: Dexie): void;

Ref - type

/**
 * Ref nominal type.
 * TS does not support nominal types. Fake implementation so the type system can match.
 */
export declare type Ref<O extends object, K extends IndexTypes, _N = "Ref"> =
  | NominalRef<O>
  | K
  | null;

Populated - type

/**
 * Overwrite the return type to the type as given in the Ref type after refs are populated.
 * T = object type;
 * B = boolean if shallow populate;
 * O = union type of object keys to populate or the string type to populate all.
 */
type Populated<T, B extends boolean = false, O extends string = string>;

Also exports some other classes and types to support further extension. See declaration or source.


Dexie.js

Dexie.js is a wrapper library for indexedDB - the standard database in the browser. https://dexie.org

/@pvermeer/dexie-populate-addon/

    Package Sidebar

    Install

    npm i @pvermeer/dexie-populate-addon

    Weekly Downloads

    62

    Version

    1.0.5

    License

    MIT

    Unpacked Size

    627 kB

    Total Files

    26

    Last publish

    Collaborators

    • pvermeer