@modelata/node-fire
This library is a wrapper around firestore to implement kind of an ODM in Node.JS projects. It also can be used in Firestore cloud functions projects.
Compatibility
firebase-admin | @modelata/fire | @modelata/node-fire |
---|
11.5.2 | ^5.0.0 | ^5.0.0 |
10.0.2 | ^4.0.0 | ^4.0.0 |
Installation
npm i -s @modelata/node-fire firebase-admin reflect-metadata
Initialization
Init firebase-admin (cf their own documentation)
import * as admin from 'firebase-admin';
const serviceAccount = require('./serviceAccount.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${serviceAccount.project_id}.firebaseio.com`,
storageBucket: `${serviceAccount.project_id}.appspot.com`,
});
const settings = { timestampsInSnapshots: true };
const db = admin.firestore();
db.settings(settings);
const auth = admin.auth();
const storage = admin.storage().bucket();
Models
Basic
Your models must extend MFModel :
export class SomeModel extends MFModel<SomeModel> {
name: string = null;
constructor(
data: Partial<SomeModel> = {},
mustachePath?: string,
location?: Partial<IMFLocation>,
) {
super();
super.initialize(data, mustachePath, location);
}
}
COMMON ATTRIBUTES
modelata-node-fire set some attribute an all models.
- _id : document id.
- _collectionPath : document path.
- _snapshot : document firestore snapshot.
- updateDate : date of last update.
- creationDate : creation date of document in db.
- deleted : boolean used for soft deletion mode
Extended
You can use decorators on properties to extend your models :
@InsubDoc
@InSubDoc('data/private')
email: string = null;
- @param subDocPath the path of the subdocument (WITHOUT main document path)
this property is from a subdocument
/!\ the DAO must extends MFFlattableDao
@AuthUserProperty
@AuthUserProperty()
email: string = null;
@StorageProperty
@StorageProperty({
deletePreviousOnUpdate: false,
deleteOnDelete: true
})
picture: IMFFile = null;
this properties links to a file stored in storage
The property must be of type : IMFFile.
DAOs
To manipulate your models in database, you need to create the corresponding DAOs :
Simple :
@CollectionPath('somes')
export class SomeDao extends MFDao<SomeModel> {
constructor(database: FirebaseFirestore.Firestore) {
super(database);
}
getNewModel(data?: Partial<SomeModel>, location?: Partial<IMFLocation>): SomeModel {
return new SomeModel(data, this.mustachePath, location);
}
}
Flattable if your model is made of data from subdocuments :
@CollectionPath('somes')
export class SomeDao extends MFFlattableDao<SomeModel> {
constructor(db: FirebaseFirestore.Firestore) {
super(db);
}
getNewModel(data?: Partial<SomeModel>, location?: Partial<IMFLocation>): SomeModel {
const someModel = new SomeModel(data, this.mustachePath, location);
return someModel;
}
}
@CollectionPath decorator
@CollectionPath('/users')
CollectionPath decorator must be used on all DAO.
CollectionPath take in parameter a string representing the collection path in firestore db.
If the collection is a subcollection (collection in a document), use the "mustache" syntaxe for all document id.
@CollectionPath('/users/{userId}/mySubCollection/{mySubDocId}/subSubcollection')
All methods that need an id or a location (like "get"), now take a Location with ids mentioned in CollectionPath.
const location = {
id: 'mySubSubDocId',
mySubDocId: 'id',
userId: 'id',
};
@DeletionMode decorator
@DeletionMode(MFDeleteMode.SOFT)
DeletionMode decorator is used for set the deletion strategy for this DAO. (default : HARD)
DeletionMode take in parameter a enum value MFDeleteMode.SOFT or MFDeleteMode.HARD.
MFDeleteMode.SOFT :
- when a dao delete a document (with delete methode), the document is just updated with delete = true;
- all getList calls have a "where filter" on deleted field
MFDeleteMode.HARD :
- when a dao delete a document (with delete methode), the document is definitely deleted.
PUBLIC METHOD
get
get(idOrLocation: string | IMFLocation, options?: IMFGetOneOptions)
Get a model from database from id or location
export interface IMFGetOneOptions {
withSnapshot?: boolean;
completeOnFirst?: boolean;
cacheable?: boolean;
warnOnMissing?: boolean;
}
getByReference
getByReference(reference: DocumentReference, options?: IMFGetOneOptions)
Get a model from database from its reference
getByPath
getByPath(path: string, options?: IMFGetOneOptions)
Get a model from database from its path
getList
getList(location?: MFOmit<IMFLocation, "id">, options?: IMFGetListOptions<M>)
Get a list of documents in the collection
export interface IMFGetListOptions<M> {
withSnapshot?: boolean;
completeOnFirst?: boolean;
where?: IMFWhere[];
orderBy?: IMFOrderBy;
limit?: number;
offset?: IMFOffset<M>;
cacheable?: boolean;
}
getReference
getReference(idOrLocation: string | Partial<IMFLocation>)
Get the reference from an id (document only), a location (document or collection) or a model (document only)
update
update(data: Partial<M>, location?: string | IMFLocation | M, options?: IMFUpdateOptions<M>)
update some field of a model.
export type IMFDeletePreviousOnUpdateFilesOptions<M extends IMFModel<M>> = {
[fileAttribute in NonFunctionPropertyNames<M>]?: boolean;
};
export interface IMFUpdateOptions<M extends IMFModel<M>> {
deletePreviousOnUpdateFiles?: IMFDeletePreviousOnUpdateFilesOptions<M>;
}
create
create(data: M, location?: string | Partial<IMFLocation>, options?: IMFSaveOptions)
save a new model in db, update if already exist.
export interface IMFSaveOptions {
overwrite?: boolean;
}
delete
delete(idLocationOrModel: string | IMFLocation | M, options?: IMFDeleteOptions<M>)
Delete a model by id
export declare type IMFDeleteOnDeleteFilesOptions<M extends IMFModel<M>> = {
[fileAttribute in NonFunctionPropertyNames<M>]?: boolean;
};
export interface IMFDeleteOptions<M extends IMFModel<M>> {
deleteOnDeleteFiles?: IMFDeleteOnDeleteFilesOptions<M>;
cascadeOnDelete?: boolean;
mode?: MFDeleteMode;
}
deleteByReference
deleteByReference(reference: DocumentReference)
Delete a model by its reference
getReferenceFromPath
getReferenceFromPath(path: string)
Get a reference from a compatible path
getSnapshot
getSnapshot(idOrLocation: string | IMFLocation, options?: IMFGetOneOptions)
isCompatible
isCompatible(modelOrReference: M | DocumentReference | CollectionReference)
Check if the model or reference is compatible with this DAO based on its path
User
User Model must implement IMFUserInterface if you want to reference auth user propertties
export class UserModel extends MFModel<UserModel> implements IMFUserInterface {
@InSubDoc('data/private')
@AuthUserProperty()
email: string = null;
@InSubDoc('data/private')
@AuthUserProperty()
phoneNumber: string = null;
@InSubDoc('data/public')
@AuthUserProperty()
photoUrl: string = null;
@AuthUserProperty()
displayName: string = null;
constructor(data: Partial<UserModel>, mustachePath: string, location: Partial<IMFLocation>) {
super();
super.initialize(data, mustachePath, location);
}
}
Usage
Once everything is properly configured you can use standard methods like create, update, delete, get, getList to interact with database.
Every async method is based on promises.
Api documentation
You can find more informations on https://moventes.github.io/modelata-node-fire/globals.html
Test
You can test with https://gitlab.com/modelata/test-node-fire