@theramagnoli/fire-base-model
v0.1.0
Published
a firestore orm
Maintainers
Readme
🔥-base-model
a lightweight, decorator-based firestore ORM for firebase. inspired by adonisjs's lucid ORM.
installation
npm install fire-base-model firebasesetup
initialize the library once, before any models are used.
import { initializeApp } from "firebase/app"
import { getFirestore } from "firebase/firestore"
import { initFireBaseModel } from "fire-base-model"
const app = initializeApp(firebaseConfig)
const db = getFirestore(app)
initFireBaseModel(db)defining models
import { FireBaseModel, property } from "fire-base-model"
export class Goal extends FireBaseModel {
static collectionName = "goals"
@property name!: string
@property description!: string
}soft deletes & timestamps
export class Post extends FireBaseModel {
static collectionName = "posts"
static softDeletes = true
static timestamps = true
@property title!: string
@property body!: string
}querying
// fetch all
const goals = await Goal.all()
// find by id
const goal = await Goal.find("abc123")
const goal = await Goal.findOrFail("abc123")
// find by field
const goal = await Goal.findBy("name", "My Goal")
// create
const goal = await Goal.create({ name: "My Goal", description: "..." })
// update
goal.name = "Updated Name"
await goal.save()
// delete
await goal.delete()
// force delete (ignores soft deletes)
await goal.forceDelete()query builder
const results = await Goal.query()
.where("status", "==", "active")
.orderBy("createdAt", "desc")
.limit(10)
.get()
// first result
const first = await Goal.query().where("name", "==", "My Goal").first()
// check existence
const exists = await Goal.query().where("name", "==", "My Goal").exists()
// count
const total = await Goal.query().where("status", "==", "active").count()
// pagination
const page = await Goal.query().paginate(10, lastId)
// page.data -> results
// page.meta.hasMore -> boolean
// page.meta.lastId -> cursor for next pagenested collections
export class Comment extends FireBaseModel {
static collectionName = "comments"
static mustHaveParent = true
@property text!: string
}
const comments = await Comment.all(parentPost)
const comment = await Comment.find("abc123", parentPost)
await Comment.create({ text: "Hello" }, parentPost)collection group queries
query across all subcollections with the same name, regardless of nesting.
const allComments = await FireBaseModelQueryBuilder.queryGroup
.call(Comment)
.where("text", "==", "Hello")
.get()mixins
import { FireBaseModel, property, type MixinConstructor } from "fire-base-model"
export function WithTimestamps<TBase extends MixinConstructor>(Base: TBase) {
class Mixin extends Base {
@property publishedAt?: Date
}
return Mixin
}
export class Post extends WithTimestamps(FireBaseModel) {
static collectionName = "posts"
@property title!: string
}infer model type
strip methods and get just the data shape of a model.
import type { InferModelType } from "fire-base-model"
type GoalData = InferModelType<Goal>
// { id: string, name: string, description: string, createdAt?: Date, ... }requirements
- firebase
>=10.0.0 - typescript
^5.0.0 experimentalDecorators: truein yourtsconfig.jsonfor legacy decorators, or typescript 5+ standard decorators work out of the box.
license
MIT
