Introducing fireorm! 🔥

Willy Ovalle
5 min readFeb 6, 2019

--

I love Firestore! Firestore is a flexible, scalable, NoSQL database for mobile, web, and server development from Firebase and Google Cloud Platform (source). I use Firestore for most of my side projects, we can create a database with a couple of clicks, quick and easy! However, more often than not I end up mixing my business logic with Firestore implementation details (plumbing) and that’s generally not a good idea. That’s why I created fireorm!

Fireorm is a tiny wrapper on top of firebase-admin (for now) that makes life easier when dealing with a Firestore database. Fireorm tries to ease the development of apps that rely on Firestore at the database layer by abstracting the access layer providing the familiar repository pattern. It basically helps us not worrying about Firestore details and focusing in what matters: adding cool new features!

Fireorm is written and can only be used in Typescript (for now). Why? Because I also love Typescript! I could tell many reasons why Typescript is awesome, but that is not the purpose of this post. Okay, I know what you’re thinking, let’s talk about the elephant in the room:

Doesn’t Typescript’s type system interfere with Firestore’s NoSQL train of thought?

First of all, having a NoSQL database doesn’t mean no schema. Apart from that, in my experience, the situations in which schemaless databases represent an advantage are pretty specific. Most of the times we need a schema in one way or another, even if is not explicit.

A NoSQL database without some controls over the data can devolve into artisanal data that dilutes the value of the data.

- Steven F. Lott, 2017

Having said that, fireorm is heavily inspired by other orms such as TypeORM and Entity Framework. The idea is that we:

1- define our model as a simple JavaScript class,
2- decorate our model with fireorm’s Collection decorator to represent a Firestore collection and
3- use our model’s repository to do CRUD operations on your Firestore database.

Did it sound like complex gibberish? It is not, I promise! Let’s explore fireorm features with an easy example: create a program to do CRUD operations into our (now fictional) rock bands database! If interested, here you can find the mock data and here is the example with all the operations!

Firestore initialization

In order to use fireorm, first we have to initialize our Firestore application. I won’t get into too much details because Firestore’s documentation has our back on that one. In the end, we’ll end up with something similar to this:

Firestore initialization

Models

Now that we have initialized our Firestore application, we can start thinking about our models. In this case, we need to hold information about rock bands: a unique id, band name, formation year and array of genres. Let’s create our model:

Band Model

Easy, huh? It’s just a Javascript Class! The only constraint here is that each model must have a string field named id.

Collections

Great, we have a model, but how can we ‘take’ our model and ‘store’ it the database? In Firestore we store data in ‘Documents’ (which is just a set of key-values) and those ‘documents’ are organized into Collections. To represent a Collection we can just ‘decorate’ our model with fireorm Collection decorator and each instance of said model would act as a Firestore Document.

Collection declaration

The Collection decorator links our Band model to a Firestore Collection. If no collection name is passed to the decorator, the collection name will be inferred by pluralizing the model’s name. If we want a custom collection name, we can pass it as the decorator’s first parameter. For example, in @Collection('rockbands') the Firestore Collection of our Bands model will be called rockbands.

Repositories

We have our models, we have our collections, but how are we supposed to make CRUD operations? That’s what Repositories are for.

In general, repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer (source).

Fireorm’s Repositories provide the necessary methods to create, retrieve, update and delete documents from our Firestore collections. To create a repository from a collection we can just call fireorm’s getRepository method. Note: in order to use a repository, the model has to have the Collection decorator, otherwise an error will be thrown.

Repository CRUD operations example

Fireorm also helps us with the creation of simple and compound queries with the addition of whereEqualTo, whereGreaterOrEqualThan, whereGreaterThan, whereLessOrEqualThan, whereLessThan, and whereArrayContains methods. We can pipe as many methods as we need to perform complex queries, as long as we don’t forget to call the find method at the end.

Queries

Please be aware that fireorm cannot circumvent Firestore query limitations, we still have to create indexes if we want to create queries that involve more than one field.

Subcollections

Fireorm supports Firestore Subcollections. In order to add a Subcollection to our Collection, we add a Subcollection property of type ISubCollection<T> with a SubCollection Decorator (where T is the model of our SubCollection and the decorator’s first parameter). As with the Collection Decorator, the Subcollection’s name will be inferred and can be overridden with the decorator’s second parameter.

For example, let’s create an Albums model and add it as a Subcollection of Band:

Subcollection example

In this case we created a model called Album to store each album information: a unique id (remember, models must have an id by design!), album name and album year. Once the model is created, we add aalbumsproperty to the existingBand model and decorate it using fireorm’s SubCollection decorator passing Album model as the first parameter.

Limitations

Subcollections can only be used on instances returned by fireorm. For example, if we want to get all the Red Hot Chili Peppers albums released after year 2000, we have to retrieve the band information first and then lookup the Album Subcollection:

Hopefully, this limitation will be gone by v1.0.0, so stay tuned!

Custom Repositories

Right now, our repositories only have CRUD methods. What if we want to add more data access logic? Fireorm supports Custom Repositories. A Custom Repository is a class that extends BaseRepository<T>(where T is a model) and is decorated with fireorm’s CustomRepository decorator.

Now, getRepository(Band) will return a custom repository with the getProgressiveRockBands method. If a model doesn’t have a custom repository, the base repository will be returned. Fireorm also provides getCustomRepository and getBaseRepository helpers if we don’t want the default behavior.

Documentation

You can learn more about fireorm features by exploring the different examples in the github repo. You can also take a look at the api docs.

Coming soon

You can see upcoming features in the roadmap.

Special thanks

Thanks to Maximo Dominguez for helping me overcome all the designs challenges when writing this library.

Me kité (🇩🇴slang).

--

--