doculite

repository·main·Indexed 18 days ago

https://github.com/stefanbielmeier/doculite

A TypeScript library that provides a Firebase Firestore-like API for SQLite. It enables developers to manage collections and documents with support for real-time listeners via .onSnapshot(), basic equality queries using .where(), and flexible document updates with merge or overwrite modes.

Tokens
4.9K
Snippets
28
Records
29
Agent score
63%

What's inside doculite

  1. Initialize a Doculite Database

    main

    To start using Doculite, instantiate a new Database object. By default, this creates a sqlite.db file in your current working directory.

    import { Database } from "doculite";
    
    // Creates sqlite.db file in the cwd
    const db = new Database();
  2. Create Collections and Set Documents

    main

    Doculite uses a Firestore-like syntax for data management. Collections are automatically created as SQLite tables upon the first document insertion.

    To interact with data, you first create a reference to a document using db.collection(name).doc(id). If you do not provide an ID to .doc(), the ID is optional (though the example suggests it may be generated or handled by the library). Documents must be valid JavaScript objects that can be parsed to JSON.

    // create ref to the doc. Doc ID optional.
    const usersRef = db.collection("users").doc("123");
    const refWithoutId = db.collection("users").doc();
    
    // Any valid Javascript object that can be parsed to valid JSON can be inserted as a document.
    await usersRef.set({
      username: "John Doe",
      createdAt: "123",
      updatedAt: "123",
    });
    await refWithoutId.set({ username: "Jane Doe" });
  3. Delete Documents in a Collection

    main

    To remove a document from a collection, call the .delete() method on its document reference.

    const db = new Database();
    const ref = db.collection("users").doc("deletable");
    
    await ref.set({ username: "deletableUsername", updatedAt: 123123 });
    
    await ref.delete();
    
    const doc = await ref.get();
    console.log(doc); // prints null
  4. Get a particular document

    main

    To retrieve the data of a specific document, create a document reference and call the .get() method. This returns the document data or null if it does not exist.

    // define ref
    const usersRef = db.collection("users").doc("123");
    // get
    const user = await usersRef.get();
    // print
    console.log(user); // prints { username: "John Doe" };
  5. Update Documents in Collections

    main

    You can update documents using the .set(data, options) method on a document reference.

    • Merge Mode (Default): If { merge: true } is passed (or omitted), properties existing in both the old and new object are updated, and properties only in the new object are added. Existing properties not present in the new object are preserved.
    • Overwrite Mode: If { merge: false } is passed, the document is replaced by the new object, and properties present in the old object but missing from the new one are deleted.
    // ref
    const usersRef = db.collection("users").doc("123");
    
    // Properties existing on both old and new object will be updated.
    // Properties only existing on the new object will be added.
    // If merge is false, properties only present on the old object will be deleted.
    // Merge is true by default
    
    await usersRef.set({ username: "DERP Doe", updatedAt: "345" }, { merge: true });
    // document in DB is now { username: "DERP Doe", updatedAt: "345", createdAt: "123" }
    
    await usersRef.set({ username: "DERP Doe", updatedAt: "345" }, { merge: false });
    // document in DB is now { username: "DERP Doe", updatedAt: "345" }
  6. Query Documents by equality comparison

    main

    You can perform basic queries on a collection using the .where(field, value) method. This returns a query object. Calling .get() on the query object returns an array of documents that match the criteria.

    const usersRef = db.collection("users");
    
    await usersRef.doc().set({ username: "Doculite", updatedAt: 234 });
    
    const query = usersRef.where("username", "Doculite");
    
    const docs = await query.get();
    
    const user = docs[0];
    console.log(user.username); // prints `Doculite`
  7. Listen to real-time updates of documents

    main

    Doculite supports real-time listeners on documents. Use the .onSnapshot(callback) method on a document reference. The callback is triggered whenever the document changes.

    Calling .onSnapshot() returns an unsubscribe function which, when executed, stops the listener.

    // ref to doc
    const ref = db.collection("users").doc("123");
    
    // snapshot listener returns unsubscribe function
    const unsub = ref.onSnapshot((doc) => {
      console.log("Omg the user doc is updating!", doc?.username);
    });
    
    await ref.set({ username: "SHEESH Doe", updatedAt: 2 });
    // prints: `Omg the user doc is updating! SHEESH Doe`
    
    // unsub
    unsub();
  8. Interact with specific documents using DocumentReference

    main

    The DocumentReference class provides a targeted API to manage a single document within a specific collection. It is initialized with a Database instance, a collectionName, and a docId.

    Key capabilities include:

    • Retrieving data: Use .get() to fetch the document content or .getRowId() to get the underlying database row ID.
    • Writing data: Use .set(docData, options) to create or overwrite a document. If the document exists, you can use the merge: true option to perform a shallow merge of the new data with the existing document.
    • Deleting data: Use .delete() to remove the document from its collection.
    • Real-time updates: Use .onSnapshot(callback) to subscribe to changes. The callback is triggered whenever the specific document is updated in the database.
    // Assuming db is an initialized Database instance
    const docRef = new DocumentReference(db, 'my_collection', 'my_doc_id');
    
    // Get the document
    const data = await docRef.get();
    
    // Set/Overwrite document
    await docRef.set({ name: 'New Name', status: 'active' });
    
    // Set/Merge document
    await docRef.set({ status: 'updated' }, { merge: true });
    
    // Listen for real-time changes
    docRef.onSnapshot((snapshot) => {
      console.log('Document changed:', snapshot);
    });
    
    // Delete the document
    await docRef.delete();
  9. Use the Database class

    main

    The primary entry point for Doculite is the Database class. It is used to manage database initialization, collection management, document operations (CRUD), and real-time updates. You can import it directly from the root module.

    import { Database } from './index';
    
    // Usage involves initializing the Database instance
  10. Execute equality queries with the Query class

    main

    The Query class allows you to fetch documents from a specific collection where a property matches a given value. You instantiate it by providing the Database instance, the collectionName, the property name to filter by, and the propertyValue to match.

    Use the .get() method to perform an asynchronous fetch of the matching documents.

    // Assuming 'db' is an initialized Database instance
    const query = new Query(db, 'users', 'email', 'user@example.com');
    const docs = await query.get();
  11. Configure document merging with set() options

    main

    When using DocumentReference.set(), you can control whether the new data overwrites the existing document or merges with it using the SetOptions object.

    • merge: false (default): The document is replaced by the new docData. Any existing fields not present in docData will be lost.
    • merge: true: The new docData is shallowly merged into the existing document. Existing fields are preserved unless they are explicitly overwritten by keys in docData.
    // Overwrite existing document
    await docRef.set({ field: 'value' }, { merge: false });
    
    // Merge new fields into existing document
    await docRef.set({ newField: 'newValue' }, { merge: true });
  12. Create a type-safe PubSub instance with PubSub()

    main

    Use the PubSub<E>() function to create a new publish-subscribe instance. The generic type parameter E defines the schema of available events, where each key in E represents an event name and its corresponding value represents the payload type for that event. This ensures type safety when publishing messages or subscribing to specific events.

    type Events = {
      warn: { message: string },
      error: { message: string }
    }
    
    const pubSub = PubSub<Events>();
    
    // Publishing an event
    pubSub.publish('warn', { message: "Something bad happened!" });