npm.io
0.1.0 • Published 4 years ago

@typeheim/orm-on-fire

Licence
MIT
Version
0.1.0
Deps
2
Size
158 kB
Vulns
0
Weekly
0

NPM Version Build Status Package License Discord

ORMOnFire is a powerful Firestore ORM.

Installation

Install package

# in backend
yarn add firebase firebase-admin firebase-tools

# in frontend
yarn add firebase 

yarn add @typeheim/orm-on-fire
# or
npm -i @typeheim/orm-on-fire

Setup ORMOnFire driver:

// sample for Node.JS
FirebaseAdmin.initializeApp({
    credential: FirebaseAdmin.credential.cert('my.key.json'),
    databaseURL: "https://my-db.firebaseio.com",
})
OrmOnFire.driver = FirebaseAdmin.firestore()

Easy entity declaration

To define entity you need to use @Entity or @Agregate decorators for simple and nested collections. Note both decorators will transform class name to kebab case - lowercase and split words with hyphens, like UserFiles < => user-files.

Then, each document field must be decorated with @Field, @MapField, @CreatedDateField, @UpdatedDateField or @DocRef(for document references) decorators. Sub-collections can be referenced by @CollectionRef decorator.

import {
    Agregate,
    Entity,
    Collection,
    CollectionRef,
    ID,
    Field,
    CreatedDateField,
    UpdatedDateField,
    MapField
} from '@typeheim/orm-on-fire'
import { CreatedDateField } from './Entity'

@Agregate()
export class User {
    @ID() id: string

    @Field() firstName: string

    @Field() lastName: string

    @Field() status: string

    @CollectionRef(UserFile) files: Collection<UserFile>
}

@Entity({ collection: 'user-files' })
export class UserFile {
    @ID() id: string

    @Field() name: string

    @MapField() properties: FileProperties

    @CreatedDateField() createdAt: Date

    @UpdatedDateField() createdAt: Date
}

class FileProperties {
    type: "image" | "doc"
}

Simple data fetching

import { Collection } from '@typeheim/orm-on-fire'

// with promise-like interface
let markus = await Collection.of(User).one('markus').get()

// with Rx interface
Collection.of(User).one('tom').get().subscribe((tom: User) => {
    tom.files.forEach((file: UserFile) => {
        // some cool stuff
    })
}) 

Powerful filtering

Using firestore operators
import { Collection } from '@typeheim/orm-on-fire'
const UsersCollection = Collection.of(User)

// Search using regular Firesotre operators
let activeUsers = await UsersCollection.all().filter(user => user.status.equal('active')).get()
let notActiveUsers = await UsersCollection.all().filter(user => user.status.notEqual('active')).get()
let adultUsers = await UsersCollection.all().filter(user => user.age.greaterThan(18)).get()

To use text index search you first need to add text index hook Firebase function to your Firebase functions list for each colelction you want to be idndexed

import { TextIndex } from '@typeheim/orm-on-fire'
import * as functions from 'firebase-functions'
import * as FirebaseAdmin from 'firebase-admin'

FirebaseAdmin.initializeApp()

export const generateUserIndex = TextIndex(functions, FirebaseAdmin).forCollection('users')
                                                     .fields(['name', 'text'])
                                                     .buildTrigger()

Official functions deployment guide: Get started: write, test, and deploy your first functions

Once you deploy hooks, you can use index search as below:

// Note: text index search is case-insensitive 
let usersStartsWithAlex = await UsersCollection.all().useIndex(user => user.firstName.startsWith('Alex')).get()
let usersEndsWithLex = await UsersCollection.all().useIndex(user => user.firstName.endsWith('lex')).get()

NOTE: for now text index won't work with collection group queries. Support coming in next releases.

Filter scopes:

Commonly used filer conditions can be organized in named filter scopes for easy code reuse:

class UserScope {
    static active() {
        return (user: EntityFilter<User>) => {
            user.status.equal(1)
        }
    }
}

// fetch all active users
let activeUsers = await UsersCollection.all().filter(UserScope.active()).get()

Sub-collection queries:

For nested collections you don't need to fetch each document separately and can access required collection under specific document ID:

// fetch all PDF files from user suwth id "userId"
let userFiles = await UsersCollection.one('userId').collecction(UserFile).filter(UserFile.pdf()).get()

Group collection queries:

ORMOnFire support easy declaration of collection groups the same way as for regular collections.

// fetch all file attachments that exist in any collection and sub-collection
let attechments = await Collection.groupOf(Attachment).all().filter(Attachment.file()).get()

Keywords