This is Part 3 in my first “20 Minutes of Code” series (if you need to, you can catch up on Part 1 and/or Part 2). We have a TaskJournal application that we’re trying to send out into the interwebs. Last time, we created a Firebase project and used it to ship our production-ready files (generated by
create-react-app) out in the world. Today, our goal is to add data persistence (saving the data) with Firebase’s included document database: Firestore. But, you know… software. So who knows where we will end up.
We have a couple options. One: we could just save directly to the database. This is kind of how Firebase intends for you to use it. You can set up permissions such that only authenticated users can see certain things in your database. You can even set up slightly more complex rules that will allow user-level distinctions for who can read and write to what documents.
Second: we could operate in a slightly more traditional web application strategy. Firebase allows you to create “serverless” functions. Because you can use Express, this means that Firebase will publish HTTP endpoints for your application. We can use this service to create a plain old RESTful API to save and retrieve data.
Remember that there are rarely “correct” answers in software. There are only obviously wrong answers and solutions with trade-offs. Let’s examine the trade-offs here.
With direct access to Firestore, we can write code more quickly. There is less to think about because the client-side Firestore library will allow us to configure rules. One downside is that this will constrain the user-experience of our application. Users will have to log in to use it and be able to keep their data private and tamper-proof. Another problem is that we will be very tightly coupled with the Firebase architecture.
With a RESTful API backing the user interface, we can structure our application independently of Firebase tooling. Specifically, right now the TaskJournal does not require a user to log in to an account. If you visit without providing a unique identifier for your existing Journal, then you are simply assigned a new one. This is a great way to keep users engaged… so long as they remember to bookmark their Journal.
With a RESTful API, we can continue to use this user experience without a problem. In other words, we don’t need to follow the strictures of the Firestore security rules. Note that I’m sure there is some pile of Firestore rules that could accomplish this same thing for us, but the point is… I don’t want to spend a bunch of time figuring that out just to have to throw it out later, which brings me to another upside.
Another benefit is that we are not necessarily tied to the Firebase architecture. We can host our RESTful API wherever we want, and we don’t have to worry about porting Firestore security rules somewhere else. The rule structure is fairly unique, and it would not be a fun rewrite. One downside is that we will have to write some more code to create our API. There is also the potential that firebase function execution time could result in us being billed a significant amount of money.
After weighing these options, we decided to use a RESTful API instead of attempting to construct Firestore-specific security rules.
When we used
firebase init in Part 1, the utility created a small starter file for serverless functions. So the first thing we have to do is find them. Just hop into the
functions directory and find your index file.
We’ll want to use express to handle our HTTP interface, so we’ll use
npm install expressto start that process. We then imported it into the file and threaded the express app into the Firebase
To make sure that everything was working like we thought it was, we copied our static JSON data from our application and pasted it into the endpoint. This will return the static JSON from the endpoint instead of reading it from the file.
Once that small test was ready, we used
firebase deploy --only functions to deploy just our new function out to Firebase.
Firebase recognized the function in our file and created an HTTP endpoint for it.
We simply navigated the browser to that endpoint. The JSON downloaded just as we expected. Now we’re ready to wire the endpoint up to Firestore and start working with real data.