Wondering what’s next for npm?Check out our public roadmap! »

    @mkeen/rxcouch
    TypeScript icon, indicating that this package has built-in type declarations

    4.8.4 • Public • Published

    RxCouch

    📀 Universal

    RxJS powers this real-time CouchDB client written in TypeScript that runs in the browser or nodejs. The change feed is handled automatically. Everything's a BehaviorSubject, so you get two-way binding for free. Enough said.

    Prerequisites

    RxJS 6+
    CouchDB 2.3+, CouchDB 3.0+

    Installation

    NPM: npm install @mkeen/rxcouch
    Yarn: yarn install @mkeen/rxcouch

    Developer Documentation

    Including RxCouch in Your Project

    import { CouchDB } from '@mkeen/rxcouch';

    Initialize RxCouch for Connecting to a CouchDB Database

    const couchDbConnection = new CouchDB(
      {
        dbName: 'people',
      }
    );

    Basic Configuration Options

    CouchDB is initialized with: (RxCouchConfig, AuthorizationBehavior?, Observable<CouchDBCredentials>?)

    An RxCouchConfig looks like this by default:

    { host: 'localhost'
    port: 5984
    ssl: false
    trackChanges: true, dbName: '_users' }

    Configuring Authentication

    CouchDB supports Basic Auth, Cookies, and JWT authentication schemes. This library supports Cookies or JWT (bearer token). For development purposes only, this library also supports open installs of couchdb without security.

    Let's take a look at how to initialize RxCouch for connecting to a CouchDB database that has Cookie Authentication configured.

    import { BehaviorSubject } from 'rxjs';
    import { CouchDB,
             CouchSession,
             AuthorizationBehavior } from '@mkeen/rxcouch';
    
    const couchSession: CouchSession = new CouchSession(
      AuthorizationBehavior.cookie,
      `${COUCH_SSL? 'https://' : 'http://'}${COUCH_HOST}:${COUCH_PORT}/_session`,
      new BehaviorSubject({username: 'username', password: 'password'}}),
    );
    
    const couchDbConnection = new CouchDB(
      {
        dbName: 'people',
        host: 'localhost',
        trackChanges: true,
      },
      couchSession
    );
    
    //...

    The big detail to note here is that the final argument passed to the CouchSession initializer is an Observable. In this example it's hardcoded. In a real-world implementation, you'll create an Observable that emits when a user submits a login form, or when a configuration value is read, and pass that Observable into the initializer.

    It's common and recommended to share a single session instance across several CouchDB instances.

    Since we're hardcoding the credentials Observable argument, the above example will result in an authentication attempt being made to the speficied CouchDB host (without using HTTPS) immediately.

    Getting Info About the Current Session

    To determine which user (if any) is currently logged into CouchDB, you can call the session() function. session returns an Observable that emits a CouchDBSession.

    //...
    couchDbConnection.session().subscribe((session: CouchDBSession) => {
      console.log('Currently session info: ', session);
    });

    If you're using CouchDB to authenticate users in your application, you could call session when your app is initialized in order to determine if a user is logged in, and if so, any other information stored in the _users document.

    Dealing with Documents

    Whether authentication is used or not, a document is always returned to you by RxCouch in the form a BehaviorSubject which provides a real-time two-way data binding with the document.

    Subscribe to Any Document in Real Time

    interface Person {
      name: string;
      email?: string;
      phone?: string;
    }
    
    const myDocument: BehaviorSubject<Person> = couchDbConnection.doc('778...05b');
    
    myDocument.subscribe((document: Person) => {
      console.log('Most recent person: ', document);
    });
    
    //...
    Result
    Most recent person: { _id: "7782f0743bee05005a548ba8af00205b", _rev: "10-bcaab49ec87c678686984d1c4873cd3e", name: 'Mike" }

    Update The Same Document in Real Time

    //...
    const myCompletelyChangedDocument = {
      name: 'some new name here',
      email: 'mwk@mikekeen.com',
      phone: '323-209-5336'
    }
    
    myDocument.next(myCompletelyChangedDocument);
    All Connected Clients Result:
    Most recent person: { _id: "7782f0743bee05005a548ba8af00205b", _rev: "11-bf3003bb5f63b875db4284f319a0b918", name: "some new name here", email:  "mwk@mikekeen.com", phone: "323-209-5336"}

    Creating And Modifying Documents

    In the above example, a document was fetched by its _id. The doc() function alternatively accepts an object as an argument. If an object is passed in, one of two things will happen:

    1. If the object contains both an _id and _rev field, the rest of the fields in the object will be used to update the document that matches the _id.
    2. If the object does not contain both an _id and a _rev field, then a new document will be created based on the fields contained in the object passed.

    Whichever of the above happens, doc returns a BehaviorSubject that will reflect all future changes to the relevant document, and pass all changes upstream when .next() is called.

    Create a New Document and Subscribe to Future Changes

    //...
    const newlyCreatedPerson = couchDbConnection.doc({
      name: 'tracy',
      email: 'tracy@company.com',
      phone: '323-209-5336'
    }).subscribe((doc: Person) => {
      console.log('Person Feed: ', doc);
    });
    Result
    Person Feed: {"_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "1-2fee21d51258845545ea6506ab138919", "name": "tracy", "email": "tracy@company.com", "phone": "323-209-5336"}

    Modify an Existing Document and Subscribe to Future Changes

    There are two ways to modify a document that already exists in CouchDB.

    One way, is to simply pass a modified version of the existing document to doc like in the example below:

    //...
    couchDbConnection.doc({
      _id: '7782f0743bee05005a548ba8af00b4f5',
      _rev: '1-2fee21d51258845545ea6506ab138919',
      name: 'tracy',
      email: 'tracy@company-modified.com',
      phone: '323-209-5336'
    }).subscribe((doc) => {
      console.log('Document Modified: ', doc);
    })
    Result
    Document Modified: { "_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "2-e654adf15f28b99f26ae1f0dbe8e7c36", "name": "tracy", "email": "tracy@company-modified.com", "phone": "323-209-5336" }
    
    Person Feed: {"_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "2-e654adf15f28b99f26ae1f0dbe8e7c36", "name": "tracy", "email": "tracy@company-modified.com", "phone": "323-209-5336" }

    Another way to modify a document that already exists in CouchDB is to just call next on an already fetched document's BehaviorSubject.

    //..
    newlyCreatedPerson.next({
      name: 'tracy Modified!',
      email: 'tracy@company-modified-again.com',
      phone: '323-209-5336'
    });
    Result
    Person Feed: {"_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "3-8da970f593132c80ccee83fc4708ce33", "name": "tracy Modified!", "email": "tracy@company-modified-again.com", "phone": "323-209-5336" }

    Finding Documents With a Query

    RxCouch exposes find from CouchDB that uses CouchDB Selector Syntax to make queries, and returns a list of matching documents. Warning: Documents returned are not BehaviorSubjects!

    Get a List of Documents That Match a Query

    Call find with (CouchDBFindQuery).

    An CouchDBFindQuery, when passed into find, should conform to CouchDB Selector Syntax. It can have the following optional properties of the following types:

    selector: CouchDBFindSelector limit: number skip: number sort: CouchDBFindSort[] fields: string[] use_index: string | [] r: number bookmark: string update: boolean stable: boolean stale: string execution_stats: boolean

    A call to find will return an Observable that will emit a list of documents that match the passed query.

    //...
    couchDbConnection.find({
      selector: {
        name: 'tracy'
      }
      
    }).subscribe((matchingPeople: Person[]) => {
      console.log('Matching people: ', matchingPeople);
    });
    Result
    Matching people: [{ "_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "3-8da970f593132c80ccee83fc4708ce33", "name": "tracy Modified!", "email": "tracy@company-modified-again.com", "phone": "323-209-5336" }]

    Get Real-Time Feeds for Found Documents

    //...
    couchDbConnection.find({
      selector: {
        name: 'some new name here'
      }
      
    }).subscribe((matchingPeople: Person[]) => {
      const documentFeeds = matchingPeople.map((person: Person) => {
        return couchDbConnection.doc(matchingPeople)
      });
      
      documentFeeds.forEach((feed) => {
        feed.subscribe(
          (document: Person) => console.log('Latest person: ', document)
        );
        
      });
      
    });
    Result
    Latest person: { "_id": "7782f0743bee05005a548ba8af00b4f5", "_rev": "3-8da970f593132c80ccee83fc4708ce33", "name": "tracy Modified!", "email": "tracy@company-modified-again.com", "phone": "323-209-5336" }

    🚂 Under the Hood

    Document Tracking

    Documents are automatically cached and then tracked for changes. CouchDBDocumentCollection handles both caching and change tracking.

    Cache

    Instances of CouchDB have a method called doc. Any documents that flow through doc are cached in a CouchDBDocumentCollection. They can later be retrieved by _id in the form of a BehaviorSubject that supports two way binding. A hash of the document is indexed (by document id) for change tracking purposes.

    Change Tracking

    Before document changes are propagated either to or from a BehaviorSubject, the potentially changed document is hashed and compared against a previously indexed hash of the document. If the hashes don't match, the document has changed, and it will be passed into the next method of the relavent indexed BehaviorSubject. Finally, the indexed hash entry for the document will be updated.

    Thank you for using RxCouch!

    🇺🇸

    Install

    npm i @mkeen/rxcouch

    DownloadsWeekly Downloads

    30

    Version

    4.8.4

    License

    ISC

    Unpacked Size

    126 kB

    Total Files

    46

    Last publish

    Collaborators

    • avatar