Building a “Serverless” RESTful API with Cloud Functions, Firestore and Express

Dale Nguyen
ITNEXT
Published in
5 min readJun 28, 2018

--

It’s obviously run on ‘a’ server somewhere in this earth; however, the upside is you don’t have to configure or maintain it.

I recently got an exercise on how to build a RESTful HTTP interface, and it intrigues me that I could try to build a temporary RESTful API to test the concept. Luckily, I am working on several projects that use Firebase Cloud Functions and Cloud Firestore. And it’s totally possible to create such endpoints for testing purposes or even a full working RESTful API project. Who knows?!

So, in this project, I will use Firebase Cloud Functions, Firebase Hosting, Cloud Firestore (database) and TypeScript. Yes, you see it right, Cloud Functions does support TypeScript! That means you can take advantage of ES6+ when writing the functions.

You can follow this guidance or check the git respository of this project.

https://github.com/dalenguyen/serverless-rest-api

I will use this API to manage the contact information as an example.

Step 1: Get your firebase project ready

Well, you need to create a firebase project to work with. Remember to choose Firestore as your database.

The rules are open to public. You need implement the security for firestore before going to production.

Step 2: Initiate Firebase Hosting and Functions on your local machine

Before getting started, you need to install firebase-tools thought NPM

npm install -g firebase-tools

After that, you log in and init firebase project

firebase login
firebase init

Remember to check both Functions and Hosting. The advantage of implementing hosting is that you will have a custom URL for your API project.

Please choose TypeScript as the language for writing functions. You can choose JavaScript if you want to.

Step 3: Install package dependencies

The packages will be in the functions folder

cd functions
npm install --save express body-parser firebase-functions-helper

The firebase-functions-helper is the package that I wrote when working with Cloud Firestore. You can use the native guide if you want.

Step 4: Write your functions

The functions logic locates at functions/src/index.ts

You first need to import the necessary packages

import * as functions from 'firebase-functions';import * as admin from 'firebase-admin';
import * as firebaseHelper from 'firebase-functions-helper';
import * as express from 'express';
import * as bodyParser from "body-parser";
admin.initializeApp(functions.config().firebase);const db = admin.firestore();const app = express();
const main = express();
const contactsCollection = 'contacts';main.use('/api/v1', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: false }));
// webApi is your functions name, and you will pass main as
// a parameter
export const webApi = functions.https.onRequest(main);

In this file, we also create CRUD route for the API

// Add new contact
app.post('/contacts', async (req, res) => {
try {
const contact: Contact = {
firstName: req.body['firstName'],
lastName: req.body['lastName'],
email: req.body['email']
}
const newDoc = await firebaseHelper.firestore
.createNewDocument(db, contactsCollection, contact);
res.status(201).send(`Created a new contact: ${newDoc.id}`);
} catch (error) {
res.status(400).send(`Contact should only contains firstName, lastName and email!!!`)
}
})
// Update new contact
app.patch('/contacts/:contactId', async (req, res) => {
const updatedDoc = await firebaseHelper.firestore
.updateDocument(db, contactsCollection, req.params.contactId, req.body);
res.status(204).send(`Update a new contact: ${updatedDoc}`);
})
// View a contact
app.get('/contacts/:contactId', (req, res) => {
firebaseHelper.firestore
.getDocument(db, contactsCollection, req.params.contactId)
.then(doc => res.status(200).send(doc))
.catch(error => res.status(400).send(`Cannot get contact: ${error}`));
})
// View all contacts
app.get('/contacts', (req, res) => {
firebaseHelper.firestore
.backup(db, contactsCollection)
.then(data => res.status(200).send(data))
.catch(error => res.status(400).send(`Cannot get contacts: ${error}`));
})
// Delete a contact
app.delete('/contacts/:contactId', async (req, res) => {
const deletedContact = await firebaseHelper.firestore
.deleteDocument(db, contactsCollection, req.params.contactId);
res.status(204).send(`Contact is deleted: ${deletedContact}`);
})

Step 5: Deploy your hosting and functions

Before deploying to firebase, we need to make some changes on firebase.json

{
"functions": {
"predeploy": [
"npm --prefix ./functions/ run lint",
"npm --prefix ./functions/ run build"
]
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "/api/v1/**",
"function": "webApi"
}
]

}
}

We will map the functions to the URL, so the webApi will trigger we will call URL. (The URL is different in your project)

https://serverless-api-dn.firebaseapp.com/api/v1/**

Now, you can deploy to firebase

firebase deploy

This command will deploy hosting and functions to firebase. Next time, you just need to deploy functions only when you make some changes to your routing.

firebase deploy --only functions

Step 6: Testing your API project

I will use Postman to test the API. First, we will create a contact by sending POST request together with the body.

View all contacts with GET request

We can pass the contact Id to view only one contact

Update my email address by sending PATCH request

Finally, we can delete a contact with DELETE request

There are some response handling that need to be improved; however, we now have a working API to experiment with.

If you are interested in building API, you can take a look a my other post:

Building RESTful Web APIs with Node.js, Express, MongoDB and TypeScript

Follow me on Twitter for the latest content on Angular, JavaScript & WebDevelopment 👐

--

--