Introduction to serverless architecture with Firebase and Cloud Functions.
Tags: serverless, firebase, cloud, cloudfunctions
Published: 2023-09-01 06:44:15


Hey there, let's talk about **serverless architecture** – the game-changer for how we build and deploy applications, offering a cost-effective, scalable, and efficient solution for various use cases. In this article, we're gonna break down the nitty-gritty of serverless architecture, all the cool perks it brings, and how it's given the whole development scene a crazy makeover. So, let's dive into the world of serverless!
<br>

## What is Serverless Architecture?

Picture this: developers get to create and run apps without even thinking about setting up servers. The real MVP here? The cloud provider takes care of all the behind-the-scenes server stuff, leaving developers free to go wild with their code.This is how **serverless architecture** allows rapid development, automatic scaling, and reduced operational overhead. 
<br>

## Some key Components of Serverless Architecture
<br>

1. **Function as a Service (FaaS)**: FaaS is at the heart of serverless architecture. Developers write functions that are triggered by events, such as HTTP requests, database changes, or timers. These functions are executed in a stateless manner, allowing them to scale automatically based on demand.

2. **Backend as a Service (BaaS)**: BaaS complements FaaS by providing pre-built services, such as databases, authentication, file storage, and messaging queues. BaaS eliminates the need to build these components from scratch, enabling faster development and reducing complexity.
<br>

## Why Serverless Architecture?

1. **Cost Savings**: With serverless, you only pay for the actual usage of your functions and services, rather than paying for idle resources. 

2. **Scalability**: Serverless platforms automatically scale your functions based on the incoming workload. Whether you have 10 users or 10,000 users, the platform dynamically allocates resources to handle the traffic, ensuring optimal performance and eliminating concerns about handling sudden spikes in usage.

3. **Developer Productivity**: Serverless architecture allows developers to focus on writing application logic rather than managing infrastructure. By offloading server management and operations to the cloud provider, developers can deliver features faster, iterate quickly, and improve overall productivity.

4. **High Availability**: Serverless platforms provide built-in fault tolerance and high availability. The underlying infrastructure ensures that functions are distributed across multiple data centres, reducing the risk of single points of failure and ensuring robustness.
<br>

## Serverless Architecture with Firebase


![Firebase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vrn0toqu6x7wr87a8zr.png)

**Firebase**, developed by Google, is a prominent platform that empowers serverless computing, specifically designed for mobile and web application development. In this article, we will explore the **Firebase Emulator Suite**, a robust toolkit that equips developers with the ability to locally develop and test their Firebase applications. We will discuss the process of setting up the Firebase Emulator and constructing a **Contact Management System** utilizing Firebase **Cloud Functions**.
<br>
## Contact Management System with Firebase

Before we begin, ensure that you have the following prerequisites in place:

1. [Node.js](https://nodejs.org/en) installed on your machine.
2. [JDK](https://www.oracle.com/java/technologies/downloads/#jdk18-windows) installed on your machine.
3. Basic knowledge of Firebase concepts and the Command Line Interface (CLI).

<br>

### 1.Setting up Firebase Emulator and initialising Firebase SDK
	
Firstly, you need to ensure that you have the Firebase CLI (Command-Line Interface) installed. To do so, open your terminal or command prompt and run the following command:

```bash
npm install -g firebase-tools
```

![install firebase-tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2iyny4ekzcj267pwnkji.png)
 
To Check the version of Firebase installed, you can use the following command:

```bash
firebase ––version
```
Next, you need to login to your Firebase account. Typing the following command will redirect you to a login page, where you’ll be prompted to login.

```bash
firebase  login
```

Now, you need to make a new project in your [Firebase Console](https://console.firebase.google.com/). For this article, we have created a project contact-manage. 
Create a new directory for your project and navigate into it. For the next step, initialize Firebase in your project folder using the following command:

```bash
firebase  init
```

You will be asked to select the Firebase features you want to set up for your directory. Press Space to select features, and then press enter. For this project, we have only chosen the ‘function’ feature. You will also be asked to choose a project and a language to write the cloud functions. Select the required options to complete the initialization process.

![firebase init](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qqo9bojoz1hozbzwgnd0.png)


The Firestore emulator grants you access to a local version of your Firestore database, resembling the view you would see in the Firebase Console. However, the emulator offers faster performance compared to the online console.To install the Emulators for your app, type in the following command:

```bash
firebase init emulators
```

Then, you will be prompted for setup questions in the terminal. First, it will ask you to pick which emulators you want to use. In this article, we’ll cover Authentication, Firestore, and Cloud Functions.
After you pick the emulators you want to use, it will then prompt you to pick the ports they’ll run on. We will just go ahead with the defaults. 
It will also ask you if you want to use the emulator’s UI, pick yes, because we’ll be using that to review and interact with our firebase application.


![firebase init emulators](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rc5vzcfwn3r05tez8zz5.png)

Now, to test that everything is set up, open the terminal again in your project’s root folder, and start the emulator using:

```bash
firebase emulators:start
```
It will start your emulators and show you the emulators' information in the terminal.


![emulators' information](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x94emztl2fs6z17janr1.png)

After starting the Firebase Emulator, you can access its user interface by navigating to `localhost:4000` in your web browser. The UI provides a dashboard displaying all the active emulators you have configured. By clicking on a specific emulator, you will be directed to its corresponding page.


![emulator ui](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dmq1sjt5g16summjm7y.png)
<br>
### 2. Writing Cloud Functions

Our app will allow the user to create a new contact, fetch all the saved contacts, delete a contact, update a contact and finally, search for a contact.

Open the `index.js ` file and start with initialising the Firebase Admin SDK with the following line of code:

```javascript
 admin.initializeApp();
```


Reference to the Firestore collection ‘contacts’ for storing all the contacts:

```javascript
const contactsCollection = admin.firestore().collection('contacts');
```
We will now define all the required functions.

#### Create Contacts Function

Our first function is  `createContacts`. This function creates a new contact in the Firestore database. It listens for an HTTP request and expects the  contact details in the request body. It uses the `contactsCollection` reference to add the contact to the Firestore collection. Upon successful creation, it responds with the contact data and a status code of 201.


```javascript
exports.createContact = functions.https.onRequest(async (req, res) => {
  try {
    const newContact = req.body;
    const contactRef = await contactsCollection.add(newContact);
    const contactId = contactRef.id;
    const contact = await contactRef.get();
    res.status(201).json({id:contactId, ...contact.data()});
  } catch (error) {
    res.status(500).send('Error creating contact');
  }
});
```<br>

#### Get Contacts Function

Next, we will create a function called `getContacts`. This function retrieves all contacts from the Firestore database. It listens for an HTTP request and fetches all documents from the `contactsCollection`. It then maps the document data and responds with an array of contact objects and a status code of 200.

```javascript
exports.getContacts = functions.https.onRequest(async (req, res) => {
  try {
    const contactsSnapshot = await contactsCollection.get();
    const contacts = contactsSnapshot.docs.map((doc) => doc.data());
    res.status(200).json(contacts);
  } catch (error) {
    res.status(500).send('Error retrieving contacts');
  }
});
```<br>

#### Update Contact Function

Our next function is `updateContacts`. This function updates an existing contact in the Firestore database. It expects the contact ID in the request URL and the updated contact details in the request body. Using the `contactsCollection` reference, it locates the specific contact document and updates it with the new information. Upon successful update, it responds with a success message and a status code of 200.

```javascript
exports.updateContact = functions.https.onRequest(async (req, res) => {
  try {
    const contactId = req.params.contactId;
    const updatedContact = req.body;

    await contactsCollection.doc(contactId).update(updatedContact);
    res.status(200).send('Contact updated successfully');
  } catch (error) {
    res.status(500).send('Error updating contact');
  }
});
```<br>

#### Delete Contact Function

To delete a contact from the Firestore database, we will create a function `deleteContact`. It listens for an HTTP request with the contact ID in the URL. Using the `contactsCollection` reference, it finds the corresponding document and deletes it. If successful, it responds with a success message and a status code of 200.

```javascript
exports.deleteContact = functions.https.onRequest(async (req, res) => {
  try {
    const contactId = req.params.contactId;
    await contactsCollection.doc(contactId).delete();
    res.status(200).send('Contact deleted successfully');
  } catch (error) {
    res.status(500).send('Error deleting contact');
  }
});
```<br>

#### Search Contacts Function

Finally, we will create a function `searchContacts`. This function searches for contacts by name in the Firestore database. It expects a search term as a query parameter in the request URL. It queries the `contactsCollection` using the `where` method to find contacts with names greater than or equal to the search term. It then maps the query snapshot and responds with an array of matching contacts and a status code of 200.

```javascript
exports.searchContacts = functions.https.onRequest(async (req, res) => {
  try {
    const searchTerm = req.query.q;
    const querySnapshot = await contactsCollection.where('name', '>=', searchTerm).get();
    const contacts = querySnapshot.docs.map((doc) => doc.data());
    res.status(200).json(contacts);
  } catch (error) {
    res.status(500).send('Error searching contacts');
  }
});
```<br>

### 3. Triggering functions on auth events

<br>

#### Create User Function

We will now export a function named `addUser`. The core functionality of this function revolves around the `onCreate` event trigger. This event is fired whenever a new user account is created in Firebase Authentication. Upon activation, the function logs a message.
The function takes a single argument named `user`. This user object holds crucial information about the newly created account, including properties like uid and `email`.To provide yourself with insight into the function's execution, you can log a message to the Firebase Cloud Functions logs. 

```javascript
exports.addUser = functions.auth.user().onCreate((user)=>{
  console.log(`${user.email} has been created...`)
  return Promise.resolve
})
```<br>

#### Delete User Function

We will next define the `delUser` function, which will serve the purpose of deleting a user from our app. The core functionality revolves around utilising the `onDelete` event trigger, which is triggered whenever a user account is deleted from Firebase Authentication. Once activated, the function proceeds to execute specific tasks related to user deletion.
The function accepts a single argument named `user`. This user object encapsulates pertinent information about the deleted account, including attributes such as `email` and `uid`.


```javascript
exports.delUser = functions.auth.user().onDelete((user)=>{
  console.log(`${user.email} has been deleted...`)
  return Promise.resolve
})
```<br>

### 4. Testing the Functions

We will now see how we perform operations using the functions we created. To make HTTP requests, you will require the URLs of the functions you have created so far. These URLs will be displayed on the terminal/command prompt when you create the functions.


![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9wxffq7lbfbaekxub5hg.png)

You can add a new contact in the database using the `createContact` function. Open your terminal/command prompt and use the `curl` command to make a POST request to the URL of the `createContact` function. Pass the contact details in the body. 

```bash
curl -X POST -H "Content-Type: application/json" -d '{
    "name": "hailey",
    "email": "hailey34@example.com",
    "phone": "5432xxxxx0"
  }' YOUR_FUNCTION_URL

```

This will add a new document in the contacts collection in our Firestore database. You can check this by navigating to the emulator's UI running on `localhost:4000`.


![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1g677abk1pqavxhdcma3.png)
<br>

The next step is to test the authentication functions. We will take the `addUser` function as an example. Navigate to the Authentication section of the Emulators' UI and click on the Add User button. Fill in the required fields and add the user.

![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yown9qs1rdgqp9zbojrd.jpeg)

Adding this user will trigger our addUser function.


![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2gtbetgk1ezwh1yyqt4.jpeg)
<br>

## Challenges and Considerations

While serverless architecture offers numerous benefits, it's important to consider the following challenges:

1. **Cold Start Latency**: The first invocation of a function may experience a delay known as a "cold start" as the serverless platform provisions the necessary resources. Although the platforms are continuously improving cold start times, it's crucial to optimize your code and leverage techniques like connection pooling to mitigate this latency.

2. **Vendor Lock-In**: Adopting serverless architecture may tie your application to a specific cloud provider's platform and services.

3. **Performance Monitoring and Debugging**: As serverless abstracts away infrastructure management, monitoring and debugging can become more challenging. 


In addition to these considerations, a number of other drawbacks have surfaced. These came to light when [Amazon Prime recently declared](https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90) that it would stop using serverless technology.
Scaling limitations, potential cost inefficiencies, issues with data-intensive operations, and constraints related to real-time processing are some scenarios that can pose as hurdles. It is also important to align architecture choices with specific project requirements, and caution must be taken against extreme stances on architecture. 
A balanced and context-aware approach is essential for making effective architectural decisions.
<br>


Alright, so let's check it out – this whole serverless thing has totally shaken up the game when it comes to building apps. It's like this new era where you can develop your stuff without worrying about servers and all that complications.

Instead of pulling your hair out over server management, you can dive right into crafting your app's core. You'll be dropping features left and right, making users happy because you're lightning-fast at giving them what they want.

_Thank you for sticking with us till the end. We hope you had a great time reading this article!_

---

*Project Github repository :*
{% github https://github.com/aimanaisha/contact-manager %}

*Project collaborators :*

{% user aishaaiman %}

{% user sahadatsk_2002 %}

{% user rounak_k_jha2002 %}

Do visit our [website](https://nitdgplug.org/) and connect with us

[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/lugnitdgp)

[![Facebook](https://img.shields.io/badge/Facebook-%231877F2.svg?style=for-the-badge&logo=Facebook&logoColor=white)](https://www.facebook.com/nitdgplug)

[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/company/lugnitdgp/mycompany/)

[![Instagram](https://img.shields.io/badge/Instagram-%23E4405F.svg?style=for-the-badge&logo=Instagram&logoColor=white)](https://www.instagram.com/nitdgplug/)

---

![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0aulzqlm0yb1cw4cg2d.jpeg)


![terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmyimakua4cm8em75es5.gif)