/reactive-record

🍭 It has never been easier to work with cross-platform data using firebase, firestore and http

Primary LanguageTypeScriptMIT LicenseMIT

Reactive Record Build Status codecov Conventional Commits

🍭 It has never been easier to work with cross-platform data using firebase, firestore, http and elasticsearch.

Guide

Requirements

  • axios
  • lodash
  • moment
  • rxjs

Install lib

npm install --save @ionfire/reactive-record

Install dependencies

npm i -P axios && npm i -P lodash && npm i -P moment && npm i -P rxjs

Angular Setup

Go to firebase console > Authentication > Web Setup and then copy and paste the config object into each angular environment at src/environments

it should look like this

export const environment = {
  production: false,
  firebase: {
    apiKey: "xxx",
    authDomain: "yyy.firebaseapp.com",
    databaseURL: "https://yyy.firebaseio.com",
    projectId: "yyy",
    storageBucket: "yyy.appspot.com",
    messagingSenderId: "zzz"
  }
};

Basic usage for firebase/firestore

Service

import * as Firebase from 'firebase/app';
import 'firebase/firestore';
import { environment } from '../../environments/environment';
import { ReactiveRecord, RROptions, FirebaseConnector, FirestoreConnector } from '@ionfire/reactive-record';

export class MyService extends ReactiveRecord {
    constructor() {
        super(<RROptions>{
            collection: 'users',
            connector: {
                firebase: new FirebaseConnector(Firebase, environment.firebase),
                firestore: new FirestoreConnector(Firebase, environment.firebase)
            }
        });
    }
}

Component

import { RRResponse } from '@ionfire/reactive-record';
import { map } from 'rxjs/operators';
import { pipe } from 'rxjs';

@Component({
    selector: 'my-component',
    templateUrl: './my-component.html'
})
export class MyComponent implements OnInit {
    users$: Observable<any[]>;
    constructor(public myService: MyService) { }
    ngOnInit() {
        this.users$ = <any>this.myService
                      .find({ query: { field: 'active', operator: '==', value: true } })
                      .pipe(
                        // transform response
                        map((response: RRResponse) => response.data)
                       );
    }
}

Template

<ul>
    <li *ngFor="let user of users$ | async">
        {{user.display_name}}
    </li>
</ul>

Reactive Record Options

option type required default interface
baseURL string false null RROptions
endpoint string false null RROptions
collection string false null RROptions
driver string false firestore RROptions
timestamp boolean false true RROptions
hook object false {} RRHook
connector object false {} RRConnector

Reactive Record Methods

Almost all RR public methods must return a rxjs observable. Not all drivers are currently implemented, feel free to submit a PR.

method params return info
find *request/extraOptions/driver Observable<RRResponse> fetch all data
findOne *request/extraOptions/driver Observable<RRResponse> fetch one data
set *id/*data/driver any set data
update *id/*data/driver any set data
on *request/onSuccess/onError/driver **function fetch realtime data
get *path/extraOptions Observable<RRResponse> fetch data using http
post *path/*body/extraOptions Observable<RRResponse> post data using http
patch *path/*body/extraOptions Observable<RRResponse> patch data using http
delete *path/extraOptions Observable<RRResponse> delete data using http

* => required

** => unsubscribe function

Extra Options

Almost all of extra options is applied only when using the ClientSetup strategy.

option type required default info
ttl number false 0 time to live for cache
key string false query/path key name for cache
forceCache boolean false false force caching
forceNetwork boolean false false force network call
disableHook string[] false [] list of hook names to disable
transformCache function false (data:any)=>data transform function for cache

Advanced Usage

Provide cache strategy out of box with ClientSetup

import * as Firebase from 'firebase/app';
import 'firebase/firestore';
import { environment } from '../../environments/environment';
import { Storage } from '@ionic/storage';
import { ClientSetup, ReactiveRecord } from '@ionfire/reactive-record';

export class MyService extends ReactiveRecord {
    constructor(public storage: Storage) {
        super(new ClientSetup({
            collection: 'my-collection',   
            storage: storage,                 // storage adapter
            firebase: Firebase,               // firebase sdk
            config: environment.firebase,     // firebase web config
            ttl: 60*60                        // time to live in seconds
        }));
    }
}

Client Setup Options

Extends RROptions plus the following

option type required default interface
ttl number false 0 ClientSetupOptions
firebase class true null ---
config object true null FirebaseConfig
storage instance true null ClientStorage
version string false null ClientSetupOptions
token object false null ClientToken

Node.js Usage

Since in server we don't need to cache any data, avoid the usage of ClientSetup

const ReactiveRecord = require('@ionfire/reactive-record').ReactiveRecord;

const todoService = new ReactiveRecord({ 
    baseURL: 'https://jsonplaceholder.typicode.com',
    endpoint: '/todos'
});

// as promise
todoService.get('/54').toPromise().then(r => console.log(r.data)).catch(console.log);

// as observable
todoService.get('/54').subscribe(r => console.log(r.data), console.log);

Dev Setup

  • git clone this repo
  • cd reactive-record
  • npm install
  • npm run build:p (or :w for watch)
  • cd dist/reactive-record
  • npm link

Try locally

  • cd your-awesome-app
  • npm link @ionfire/reactive-record
  • import stuff and do amazing things

Running tests

  • cd reactive-record
  • npm run test (or test:w for watch)
  • see the coverage folder generated

Changelog

We keep our changes synced with a changelog. Check it out.

🤝 Contributions

Contributions, issues and feature requests are always welcome. Please make sure to read the Contributing Guide before making a pull request.