firebase-worm - Firebase With ORM
firebase-worm
is a lightweight Object Relational Mapping implementation for
Firebase Realtime Database.
Setting up session
When working with NoSQL databases, more often than not it is needed to update multiple objects at the same time.
Therefore, the base object to work with is a Session
.
import {Session} from 'firebase-worm';
const firebaseConfig = {
};
const app = firebase.initializeApp(firebaseConfig);
const session = new Session(app.database());
await session.commit();
session.dispose();
firebase-worm
does not initialize the firebase application. You need to do it
yourself in your preferred way, and then create a Session
instance.
The Session
accumulates the changes in memory as you work with the entities. When
Session.commit
is called, all changes are persisted to the database.
Don't forget to dispose the session when it's no longer needed, this clears the
listeners on database keys.
Defining entities
An entity is plain javascript object which is persisted to the database.
import {Entity} from 'firebase-worm';
@entity("/products")
class Product extends Entity {
name: string;
price: number;
}
Entities are modelled as classes, extending the base Entity
class.
The @entity()
decorator specifies the path in the JSON tree where the collection
is persisted.
Querying data
To query the entities, you need to create an instance of the Repository
.
The repository exposes the data as rxjs
observables. When the underlying data
changes in firebase, new values are emitted.
{
"/products": {
"product1": {
name: "Product 1"
},
"product2": {
name: "Product 2"
}
}
}
const repository = session.repository(Product);
repository.findById("product1").subscribe((product) => {
console.log(product.$id, product.name);
});
repository
.findAll(q => q.orderBy(x => x.name).equalTo("Product 2"))
.subscribe(products => {
console.log(products);
});
{
"/products": {
"product1": {
name: "Product 1",
info: {
categories: {
category1: true,
}
}
},
"product2": {
name: "Product 2",
info: {
categories: {
category2: true,
}
}
}
}
}
const categoryId = "category2";
repository
.findAll(q => q.orderBy(x => x.info.categories[categoryId]).equalTo(true))
.subscribe(products => {
console.log(products);
});
Inserting data
To insert new entities into the database, call the save
method on the repository,
and then commit the session.
@entity("/products")
class Product extends Entity {
name: string;
price: number;
}
const session = new Session(app.database());
const repository = session.repository(Product);
const product = new Product();
product.$id = "p1";
product.name = "product 1";
product.price = 100;
repository.save(product);
await session.commit();
Updating data
All entities found with Repository.find
or Repository.findAll
are tracked for
changes. When Repository.save
is called, a partial diff is calculated and stored
in memory. Session.commit
flushes all diffs to the database.
@entity("/products")
class Product extends Entity {
name: string;
price: number;
}
const session = new Session(app.database());
const repository = session.repository(Product);
repository.findById("product1").pipe(
concatMap(product => {
product.price = 200;
repository.save(product);
return session.commit();
})
).subscribe();
Deleting data
Similarly to udpates, calling Repository.delete
or Repository.deleteById
marks
the corresponding key as null
.
@entity("/products")
class Product extends Entity {
name: string;
price: number;
}
const session = new Session(app.database());
const repository = session.repository(Product);
repository.findById("product1").pipe(
concatMap(product => {
repository.delete(product);
return session.commit();
})
).subscribe();
repository.deleteById("product1");
await session.commit();