Firebase
Firestore

Calling Cloud Firestore APIs

Cloud Firestore (opens in a new tab) is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud. Ensemble platform provides a deep integration with the Firestore DB and allows you to call operations on your data in Firestore effortlessly.

Unlike traditional relational databases, Firestore offers a document-based structure well-suited for storing and managing various data types within oour app. Firestore is a great choice for Ensemble applications because it provides simplified data modeling, Offline Persistence, Real-time Updates, and API Convenience to interact with data.⁤

Now, let's dive into performing basic operations on our Firestore database:

⚠️

Operations on Firestore won't work unless we have configured our Ensemble application with Firebase. Learn how to configure it here.

1. Creating a Firestore Collection

To store data in Firestore, we need to create a collection. A collection in Firestore is a container for documents, which are individual pieces of data. Each document contains a set of key-value pairs. Follow the given steps to create a collection:

  • Go to the Firebase Console.
  • Select our project.
  • Navigate to Firestore Database in the side menu.
  • Click on Start collection.
  • Enter a collection ID (e.g., sports).
  • We can add our first document by entering a document ID or let Firestore auto-generate one for us.
  • Add fields and values to our document.
  • Click Save.

By default, firestore rules do not allow anyone to access our database. To get started with it, update the rules by changing it to allow read, write; which allow everyone to access our database and then update the rules according to our requirements.

2. Types of Firestore Operations

Firestore offers various operations to interact with our data. Here's a breakdown of some core operations along with demo API calls for our Ensemble app

Get:

This operation retrieves data from our Firestore collections. We can either retrieve entire collections or use queries to filter and sort our data.

  1. Example (Get all users):
getUsers:
  type: firestore
  path: users
  listenForChanges: true

Explanation:

  • type: firestore: Specifies that the operation is for Firestore and it is not a RestAPI.
  • path: The path to the collection or document from where we wanna retrieve the data.
  • listenForChanges: The operation will listen for real-time updates if set to true and triggers the UI to update.

Default vale for operation is set to add if not mentioned.

  1. Example (Get user using multiple filters):
getSpecificUsers:
  inputs:
    - userId
  type: firestore
  path: example/users
  query:
    where:
      - field: _documentId
        operator: ==
        value: ${userId}
    orderBy:
      - age
    limit: 10

Explanation:

Add:

The add operation creates a new document in a collection with a specified or auto-generated ID.

  1. Example:
createProject:
  inputs:
    - userId
    - proName
    - proDes
    - proFiles
  type: firestore
  path: users/${userId}/Projects
  operation: add
  data:
    proName: ${proName}
    description: ${proDes}
    createdAt: ${(new Date()).toISOString()}

Explanation:

  • operation: add: Indicates that a new document will be created.
  • data: The fields and values for the new document.

Set:

The set operation can create a new document if it does not exist but if the document already exists, set will overwrite the entire document with the data provided, unless we use the merge option.

  1. Example:
setProject:
  inputs:
    - userId
    - projectID
    - proName
    - proDes
    - proFiles
  type: firestore
  path: users/${userId}/Projects/${projectID}
  operation: set
  data:
    proName: ${proName}
    description: ${proDes}
    setAt: ${(new Date()).toISOString()}

Update:

The update operation only updates the fields specified in the provided data. If the document does not exist, update will fail with an error.

  1. Example:
inputs:
    - userId
    - projectID
  type: firestore
  path: users/${userId}/Projects/${projectID}
  operation: update
  data:
    # Below files will be stored as Array of objects.
    files: [{ name: "index.js" , lines: 78 },{ name: "LMS.js" , lines: 245 }] 
    lastUpdated: ${(new Date()).toISOString()}

Delete:

The delete operation removes a document from a collection.

  1. Example:
deleteProject:
  inputs:
    - userId
    - projectID
  type: firestore
  path: users/${userId}/Projects/${projectID}
  operation: delete

Collection Group:

The isCollectionGroup feature is used to retrieve specific collections from any collection. For example, if we have 100 documents in the users collection and each document has a sub-collection named projects, the isCollectionGroup feature helps in getting all projects directly rather than iterating through each document.

  1. Example:
getAllProjects:
  type: firestore
  path: Projects
  isCollectionGroup: true

3. Response of Firestore Operations

When performing Firestore operations, we may need to manipulate the responses to fit our application's needs. Below are some common ways demonstrating how to use YAML for API calls, handle states, and display data in our app.

1. Firstly, we will make an API call as follow:

invokeAPI:
  name: getProjects
  inputs:
    userId: ${userID}

We can also use onResponse & onError on firebase API calls and can perform operations on response.

2. Using response in Column:

To display data based on the API call's state (loading, success, error), you can use the following structure:

Column:
  children:
    - Column:
        styles:
          visible: '${getProjects.isLoading ? true : false}'
        children:
          - Progress:
              display: circular
    - Column:
        styles:
          visible: '${getProjects.isSuccess ? true : false}'
        item-template:
          data: ${getProjects.body.documents}
          name: project
          template:
            projectDisplay: # that is an custom widget.
              inputs:
                name: ${project.proName}
                des: ${project.description}
    - Column:
        styles:
          visible: '${getProjects.isError ? true : false}'
        children:
          - Text:
              text: "An error has occurred"
  • Explanation:
    • The first child Column is visible only when the API call is loading (visible: '${getProjects.isLoading ? true : false}'). It shows a circular progress indicator.
    • The second child Column is visible only when the API call is successful (visible: '${getProjects.isSuccess ? true : false}'). It iterates over the documents in the response body using item-template.
    • The third child Column is visible only when there is an error (visible: '${getProjects.isError ? true : false}'). It shows an error message.

3. Using response in Dropdown:

To display data in a dropdown, we can use the following YAML structure:

Dropdown:
  id: selectProject
  label: Select Project
  itemTemplate:
    data: ${getProjects.body.documents}
    name: project
    value: ${project._documentId}
    template:
      Text:
        text: ${"Name:" + " " + project.proName}

By using these operations, we can efficiently manage our data in Firestore with an Ensemble project. Firestore's real-time capabilities and simple API calls make it a powerful tool for any application.