GithubHelp home page GithubHelp logo

phamngocduy98 / npm-flashstore-admin Goto Github PK

View Code? Open in Web Editor NEW
2.0 1.0 1.0 304 KB

Firebase Cloud Firestore Library | For NodeJS server with FirebaseAdminSdk

TypeScript 100.00%
firebase firebase-admin firebase-admin-sdk firebase-database firebase-firestore firebase-nodejs nodejs nodejs-framework

npm-flashstore-admin's Introduction

Flashstore admin

WARNING: Flashstore is currently in early beta version which is under development, may cause unexpected behaviors and should only be used in personal project.

npm GitHub Workflow Status codecov npm npm
npm peer dependency version npm peer dependency version (scoped) node-lts (scoped)

A firebase firestore library that making it easier to CRUD data with typescript

npm i @phamngocduy98/flashstore-admin

How to use

1. Define document data type

Define properties of your document by extending DocumentData class

import {DocumentData} from "@phamngocduy98/flashstore-admin";

export class User extends DocumentData {
    constructor(public name: string, public avatarUrl: string) {
        super();
    }
}

2. Define collections:

Define your database by extending Database class. There you can define your collections using @Collection decorator:
@Collection(DocDataType: D extends DocumentData, collectionName?: string)
For example, you have a root collection whose name is users

import {Database, Collection, FirestoreCollection} from "@phamngocduy98/flashstore-admin";
import {User} from ".";

export class MyDatabase extends Database {
    @Collection(User, "users")
    public users!: FirestoreCollection<User>;
}

If you don't define collection's name parameter like @Collection(User), the library will use property's name by default.
You can define sub-collection of a document too. If you have a subcollection wells inside a Village document, then your Village class should look like this:

import {DocumentData, Collection, FirestoreCollection} from "@phamngocduy98/flashstore-admin";
import {Well} from "./sample_db/Well";

export class Village extends DocumentData {
    @Collection(Well, "wells")
    public wells!: FirestoreCollection<Well>;
}

3. CRUD:

Your need initialize your firebase app before using firestore:

import * as admin from "firebase-admin";
import {FirestoreDocument, FirestoreCollection} from "@phamngocduy98/flashstore-admin";
import {User, MyDatabase} from ".";

admin.initializeApp();
const db = new MyDatabase(admin.firestore());

3.1 CRUD a collection:

QUERY:
const userCollection: FirestoreCollection<User> = db.users;
const users: User[] = await db.users.query((ref) => ref.where("name", "==", "Duy"));
CREATE:
// return FirebaseDocument instance of newly create document
const newDocWithAutoCreatedId: FirestoreDocument<User> = await db.users.create(undefined, new User());
const newDocWithSpecificId: FirestoreDocument<User> = await db.users.create("new_user_id", new User());

// create in batch:
const batch = db.batch();
db.users.createInBatch(batch, "new_user_id", new User());
DELETE:
await db.users.delete("new_user_id");
// delete in batch:
const batch = db.batch();
db.users.deleteInBatch(batch, "new_user_id");

3.2 CRUD a document:

You need FirestoreDocument instance to read data or make changes to the document. Get a document instance by ID: Collection.document(docId)

const userCollection: FirestoreCollection<User> = db.users;
const userDoc: FirestoreDocument<User> = db.users.document("my_user_id");

Then you can read or modify to document as you want:

READ:
// (return null if document not exists)
const userData: User = await userDoc.get();
console.log(userData?.name, userData?.avatarUrl);
UPDATE:
await userDoc.update({avatarUrl: "new avatar"});
DETELE:
await userDoc.delete();
SET:
// (set will overwrite current value or create a new document if the document not exist)
userDoc.set({name: "new name", avatarUrl: "new avatar"});
Access sub-collection:
const villageDoc = await db.villages.document("test_village");
const wellSubCollection: FirestoreCollection<Well> = villageDoc.collection("wells");
// Then you can use all methods that is available for collection
await wellSubCollection.create(undefined, new Well("well 1"));

4. Using references.

4.1 Define a document property reference to other Document

For example, you have a Village entity then your village have an owner which is a user. You need create an owner property with @RefFDocument(collectionName: string) decorator.

@RefFDocument("users")
owner: FirestoreDocument<User>;

Then the owner property will be stored as DocumentReference in firestore, while you can access it as a true FirestoreDocument in Village instance.
Make sure your collection name (eg. "users") should be root collection (not a sub-collection).

export class Village extends DocumentData {
    @RefFDocument("users")
    owner: FirestoreDocument<User>;

    constructor(public name: string, public description: string, owner: FirestoreDocument<User>) {
        super();
        this.owner = owner;
    }
}
Read data of referenced document:

Read Village.owner document data:

const villageDoc: FirestoreDocument<Village> = db.villages.document("village_id");
const villageData: Village = await villageDoc.get();

const ownerDoc: FirestoreDocument<User> = village!.owner;
const ownerData: User = await village!.owner.get();
console.log(ownerData?.name, ownerData?.avatarUrl);
Modify the referenced property
Change it to another document.
const userDoc: FirestoreDocument<User> = db.users.document("user_id");
villageDoc.update({owner: userDoc});
Set it to null

Make sure you define it nullable in Village DocumentData like this owner: FirestoreDocument<User> | null;before set it to null:

export class Village extends DocumentData {
    @RefFDocument("users")
    owner: FirestoreDocument<User> | null;
}
villageDoc.update({owner: null});

4.2 Define a document property reference to array of other Documents

Continue our story: a village entity cannot only have owner :) It needs members which is an array of User. You can use @RefFDUnionArray decorator the same as @RefFDocument. The only difference is members is an array of FirestoreDocument<User> and is stored as an array of DocumentReference in firestore.

@RefFDUnionArray("users")
members: FDUnionArray<FirestoreDocument<User>>;

So, the completed Village class is:

export class Village extends DocumentData {
    @RefFDocument("users")
    owner: FirestoreDocument<User>;
    @RefFDUnionArray("users")
    members: FDUnionArray<FirestoreDocument<User>>;

    constructor(public name: string, public description: string, owner: FirestoreDocument<User>) {
        super();
        this.owner = owner;
        this.members = new FDUnionArray(owner);
    }
}
CRUD referenced document array:
const villageDoc: FirestoreDocument<Village> = db.villages.document("village_id");
const village: Village = await villageDoc.get(); // you must get() parent document before reading referenced document array
const members: FDUnionArray<FirestoreDocument<User>> = village!.members;
Get a document from array:
const village: Village = await villageDoc.get(); // you must get() parent document before reading referenced document array
const member0: FirestoreDocument<User> = await village!.members[0];
const member0Data: Village = await member0.get();
console.log(member0Data.name, member0Data.avatarUrl);
Get all array
const village: Village = await villageDoc.get(); // you must get() parent document before reading referenced document array
const villages: Village[] = await village.members.getAll();
push a element
const userDoc: FirestoreDocument<User> = db.users.document("user_id");
await village.members.pushDB(userDoc);
pop the array
const popDoc = await village.members.popDB();
Splice the array
await village.members.spliceDB(0, 1);
Update the whole array
const village1Doc: FirestoreDocument<Village> = db.villages.document("village_1_id");
const village2Doc: FirestoreDocument<Village> = db.villages.document("village_2_id");
await village.update({members: [village1Doc, village2Doc]});

You may want to explore (or not) FDArrayTracker that can be get via FirestoreDocument.linkedArray(propertyName).

5. Realtime support

WARNING: This is experiment feature.
While realtime features of firestore costs your firestore read/write quota a lot, it's recommend to avoid using it. Realtime Database is a good choice too.

5.1 Realtime Collection

Use @RealtimeCollection decorator instead of @Collection decorator to define a realtime collection. Realtime Collection document's data is kept in sync with firestore in realtime.
You can add listeners to listen these changes too.

import {OnCollectionChangedListener, RealtimeFirestoreDocument} from "@phamngocduy98/flashstore-admin";

const listener = new OnCollectionChangedListener();
listener.onDocumentAdded = (doc: RealtimeFirestoreDocument<D>) => {
    console.log(doc.value());
};
listener.onDocumentModified = (doc: RealtimeFirestoreDocument<D>) => {
    console.log(doc.value());
};
listener.onDocumentRemoved = (doc: RealtimeFirestoreDocument<D>) => {
    console.log(doc.value());
};
db.anyRealtimeCollection.addOnCollectionChangedListener(listener);

5.2 Realtime Document

RealtimeFirestoreDocument is created be RealtimeFirestoreCollection. Its data is always up to date with the server in realtime. It support listener too.`

import {OnValueChangedListener, RealtimeFirestoreDocument} from "@phamngocduy98/flashstore-admin";

const anyRealtimeDocument: RealtimeFirestoreDocument<D> = db.anyRealtimeCollection.document("document_id");

const listener = new OnValueChangedListener<D>();
listener.onValueChanged = (doc: RealtimeFirestoreDocument<D>) => {
    console.log(doc.value());
};
listener.onDocumentRemoved = (doc: RealtimeFirestoreDocument<D>) => {
    console.log(doc.value());
};
anyRealtimeDocument.addOnValueChangedListener(listener);
Realtime Document with Referenced Document Array:
Listen for changes

You can attach listener to listen when a item is being added to or removed from array

const listener = OnArrayChangedListener<User>();
listener.onItemsInserted = (docs: FirestoreDocument<User>[]) => {
    console.log(docs);
};
listener.onItemsRemoved = (docs: FirestoreDocument<User>[]) => {
    console.log(docs);
};
villageDoc.linkedArray("member").addOnArrayChangedListener(listener);

6. Batch support

let batch = db.batch(); // or batch = anyDocument.root.batch();
userDoc.updateInBatch(batch, {name: "new name"});
userDoc.updateInBatch(batch, {avatarUrl: "new avatar"});
await batch.commit();

7. Access parent and root:

In each document or collection, you can get its parent or root but I do not recommend you do so.

let root: MyDatabase = userDoc.root;
let userCollection: FirestoreCollection<User> = userDoc.parentCollection;

8. A subcollection pattern:

Reference to a sub-collection is NOT SUPPORTED in the current version of flashstore.
Please wait for updates!

Pattern introduction:

  • You want to allow clients to directly connect to firestore database.
  • For example, you allow clients to create their own document to a sub-collection. Then you want reset the sub-collection for new writes, so you need to delete each document in the sub-collection.
  • These delete operations cost a lot while after deletions, your sub-collection will be soon full of new documents.

=> So there is a solution, you can create a referenced document array to take care of which document is new. Now you can access all new documents without reset the sub-collection.

npm-flashstore-admin's People

Contributors

phamngocduy98 avatar

Stargazers

 avatar  avatar

Watchers

 avatar

Forkers

hans220117

npm-flashstore-admin's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.