Create repository classes for each collection you want to query documents from. For example
if you want to query documents to query from the users
collection you create a class UserRepository
extending BaseRepository
.
Each repository provides a list of functions for saving, querying and deleting documents
and you can extend each repository based on your needs.
When extending BaseRepository
you have to implement the function
getCollectionPath(...ids: string[])
. For root collections the ids[] will be empty.
For sub-collections this parameter will contain an hierarchically ordered list of parent
document ids.
Each function takes multiple ids as its last arguments. Those are the hierarchically
ordered list of parent document ids passed to the getCollectionPath(...)
function.
The following examples are based on the RestaurantRepository
and ReviewRepository
created below
findById
Takes a hierarchical ordered list of document ids. Returns the document when found or null
const review = await reviewRepo.findById(restaurantId, reviewId);
find
Queries the collection to match the given arguments, returns the first result or null
if none is found.
const review = await reviewRepo.find({
rating: 5
}, restaurantId);
getById
Works exactly like findById but throws an error if no document was found
get
Works exactly like find but throws an error if no document was found
list
Query a list of documents with a set of given arguments. This function always returns an array. If no results were found
the array will be empty
const allOneStarRatings = await reviewRepo.list({
rating: 1
}, restaurantId);
query
Do more complex queries like greater than
and lower than
comparisons.
const reviews = await reviewRepo.query(() => {
return qb
.where('rating', '==', 2)
.where('submitDate', '<', new Date('2019-12-31'));
}, restaurantId);
Valid operators are ==
| <
| <=
| >
| >=
QueryBuilder functions
qb.where(fieldName, operator, value)
qb.orderBy(fieldName, direction) // 'asc' or 'desc'
qb.offset(number)
qb.limit(number)
findAll
Returns an array of documents for a given array of ids. The array will contain null values if some documents aren't found
const r = await restaurantRepo.findAll([id1, id2]);
getAll
Returns an array of documents for a given array of ids. The array won't contain null values. If a document doesn't exists,
an error will be thrown
const r = await restaurantRepo.getAll([id1, id2]);
save
Saves a document into Firestore.
const restaurant = await restaurantRepo.save({
name: 'Ebi'
});
If you want to update data you just have to pass the id of the document.
const user = await restaurantRepo.save({
id: '8zCW4UszD0wmdrpBNswp',
name: 'Ebi',
openDate: new Date()
});
By default this will create the document with this id if it doesn't exist
or merge the properties into the existing document. If you want to write a document
and instead of don't merge use the [write()][write] function
write
Sets the passed data. If the document exists it will be overwritten.
const user = await restaurantRepo.write({
name: 'FreshBurgers',
openDate: new Date()
});
delete
Deletes a document by a given id
// For a nested collection
await reviewRepo.delete(restaurantId, reviewId);
// For a root level collection
await restaurantRepo.delete(restaurantId);
transaction
Takes an update function and an array of ids. Find more about transactions at the
Firestore documentation
const result = await restaurantRepo.transaction((trx) => {
const u = trx.get('some-id');
u.name = 'Burger Store';
trx.set(u);
return 'done';
})
Extending BaseRepository
export class RestaurantRepository extends BaseRepository<Restaurant> {
getCollectionPath(...documentIds: string[]): string {
return 'restaurants';
}
}
When creating repositories for nested collection it's always a good idea to check if the correct ids are passed into
getCollectionPath(...)
.
export class ReviewRepository<T> extends BaseRepository<Review> {
getCollectionPath(...documentIds): string {
const id = documentIds.shift();
if (!id) {
throw new Error('RestaurantId id is missing');
}
return `restaurants/${id}/reviews`;
}
}
This will throw an error when trying to save or query without passing the user id.
await reviewRepo.save({...}); // Throws and error
await reviewRepo.save({...}, '<restaurantId>'); // Succeeds