npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@medyll/idbql

v3.6.0

Published

idbql is a powerful TypeScript library that provides a MongoDB-like query interface for IndexedDB, simplifying client-side database operations with strong typing and reactive state management.

Downloads

10

Readme

idbql - IndexedDB Query Language

idbql is a powerful TypeScript library that provides a MongoDB-like query interface for IndexedDB, simplifying client-side database operations with strong typing and reactive state management.

Table of Contents

  1. Features
  2. Installation
  3. Quick Start
  4. API Reference
  5. Data Modeling
  6. Query Operators
  7. Event Types
  8. Indexing
  9. Transactions
  10. Reactive State Management
  11. Integration with Svelte
  12. Versioning and Migrations
  13. Error Handling
  14. Performance Optimization
  15. Contributing
  16. License

Features

  • MongoDB-like API for IndexedDB queries
  • Strong typing with TypeScript for enhanced developer experience
  • Reactive state management for real-time UI updates
  • Support for complex CRUD operations and advanced querying
  • Flexible and extensible data modeling
  • Built-in indexing and optimization features
  • Easy integration with front-end frameworks, especially Svelte
  • Robust error handling and logging
  • Versioning and database migration support

Installation

Install idbql using npm:

npm install idbql

Quick Start

Here's a basic example to get you started:

import { createIdbqDb } from 'idbql';

// Define your data model
const exampleModel = {
  messages: {
    keyPath: "++id, chatId, created_at",
    ts: {} as ChatMessage,
  },
  chat: {
    keyPath: "&chatId, created_at, dateLastMessage",
    ts: {} as Chat,
    template: {},
  },
};

// Create a database instance
const idbqStore = createIdbqDb(exampleModel, 1);
const { idbql, idbqlState, idbDatabase, idbqModel } = idbqStore.create("myDatabase");

// Perform database operations
async function fetchMessages() {
  const messages = await idbql.messages.where({ chatId: "123" }).toArray();
  console.log(messages);
}

fetchMessages();

API Reference

createIdbqDb

createIdbqDb(model: IdbqModel, version: number)

Creates an IndexedDB database instance with the specified model and version.

  • model: An object describing your database schema
  • version: The version number of your database schema

Returns an object with a create method to initialize the database.

idbql

The main interface for database operations. It provides methods for each collection defined in your model.

Example operations:

// Add a new item
await idbql.messages.add({ chatId: "123", content: "Hello" });

// Update an item
await idbql.messages.put({ id: 1, content: "Updated message" });

// Delete an item
await idbql.messages.delete(1);

// Query items
const recentMessages = await idbql.messages
  .where({ created_at: { gt: new Date(Date.now() - 86400000) } })
  .toArray();

idbqlState

A reactive state object that reflects the current state of your database. It's particularly useful for integrating with reactive UI frameworks.

idbDatabase

Provides low-level access to the IndexedDB instance, allowing for more advanced operations when needed.

idbqModel

Contains the database model definition, useful for introspection and dynamic querying.

Data Modeling

Define your data model using the IdbqModel type:

type IdbqModel = {
  [collectionName: string]: {
    keyPath: string;
    ts: any; // TypeScript type for the collection
    template?: object; // Optional default values
  };
};

Example:

const myModel: IdbqModel = {
  users: {
    keyPath: "++id, email",
    ts: {} as User,
    template: { role: "user" },
  },
  posts: {
    keyPath: "++id, userId, createdAt",
    ts: {} as Post,
  },
};

Query Operators

idbql supports a wide range of query operators:

  • eq: Equal to
  • ne: Not equal to
  • gt: Greater than
  • gte: Greater than or equal to
  • lt: Less than
  • lte: Less than or equal to
  • in: In array
  • nin: Not in array
  • contains: String contains
  • startsWith: String starts with
  • endsWith: String ends with
  • btw: Between (inclusive)

Example usage:

const results = await idbql.users.where({
  age: { gte: 18, lte: 65 },
  name: { startsWith: "A" },
  role: { in: ["admin", "moderator"] },
}).toArray();

Event Types

idbql uses a set of event types to represent different database operations:

/**
 * Represents the types of database operations that can be performed.
 * 
 * @typedef {string} EventType
 * @property {string} add - Add a new record
 * @property {string} put - Put (insert or update) a record
 * @property {string} update - Update an existing record
 * @property {string} updateWhere - Update records that match a condition
 * @property {string} delete - Delete a specific record
 * @property {string} deleteWhere - Delete records that match a condition
 * @property {string} set - Set the value of a record
 */
export type EventType =
  | "add"
  | "put"
  | "update"
  | "updateWhere"
  | "delete"
  | "deleteWhere"
  | "set";

These event types are used internally and can be useful when working with database change events or custom middleware.

Indexing

Indexing is crucial for query performance. Define indexes in your model's keyPath:

const model = {
  users: {
    keyPath: "++id, email, &username, *tags",
    // ...
  },
};
  • ++id: Auto-incrementing primary key
  • email: Regular index
  • &username: Unique index
  • *tags: Multi-entry index for arrays

Transactions

idbql supports complex transactions across multiple object stores, ensuring data consistency and atomicity. Here's how you can use transactions:

Basic Transaction

const result = await idbql.transaction(
  ["users", "posts"],
  "readwrite",
  async (tx) => {
    const userStore = tx.objectStore("users");
    const postStore = tx.objectStore("posts");

    const userId = await userStore.add({ name: "Alice", email: "[email protected]" });
    const postId = await postStore.add({ userId, title: "Alice's First Post", content: "Hello, World!" });

    return { userId, postId };
  }
);

console.log("New user ID:", result.userId);
console.log("New post ID:", result.postId);

Reactive State Management

idbqlState provides a reactive interface to your data:

import { derived } from 'svelte/store';

const activeUsers = $derived(idbqlState.users.where({ isActive: true }));

Integration with Svelte

Svelte 5 example:

<script>
import { derived } from 'svelte/store';
import { idbqlState } from './store';

const messages = $derived(idbqlState.messages.where({ chatId: "123" }));
</script>

{#each $messages as message}
  <p>{message.content}</p>
{/each}

Versioning and Migrations

Handle database schema changes with versioning:

const idbqStore = createIdbqDb(myModel, 2);
const { idbDatabase } = idbqStore.create("myDb", {
  upgrade(oldVersion, newVersion, transaction) {
    if (oldVersion < 2) {
      const userStore = transaction.objectStore("users");
      userStore.createIndex("emailIndex", "email", { unique: true });
    }
  },
});

Error Handling

idbql provides detailed error information:

try {
  await idbql.users.add({ username: "existing_user" });
} catch (error) {
  if (error instanceof UniqueConstraintError) {
    console.error("Username already exists");
  } else {
    console.error("An unexpected error occurred", error);
  }
}

Performance Optimization

  • Use appropriate indexes
  • Limit result sets with .limit(n)
  • Use .count() instead of .toArray().length
  • Optimize queries to use indexes effectively

Contributing

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.

License

This project is licensed under the MIT License.