Search code examples
node.jsgoogle-cloud-firestorefirebase-admin

How to update the only changed fields in Firestore?


In the users collection in Firestore, I have all users' uid as documents, inside each user document I am storing user preferences.

For example, here's a sample of user preferences I am saving in a specific user document:

{
  "preferences": {
    "settings": {
      "themeColorMode": "light-mode",
      "debugMode": false
    },
    "filterChips": {
      "pathName": {
        "filterChipsPreferences": true
      }
    }
  }
}

I want to update user document with the data sent in the body of an API

I want that API to be compatible in a way such that

  • I should be able to add another root node other than preferences
  • I should be able to customize and add new nodes in preferences.settings & preferences.filterChips
  • I should be able to update a specific node - examples: preferences.settings.themeColorMode& preferences.filterChips.filterChipsPreferences

For example, in the request body I am sending this info:

{
  "preferences": {
    "settings": {
      "themeColorMode": "dark-mode",
      "isSoundNotificationOn": false,
      "isAppListeningToStream": true
    },
    "filterChips": {
      "pathName": {
        "filterChipsPreferences": false
      },
      "FirstUsedSearch": "23/12/2021"
    },
    "columnDefs": {
      "pathName": {
        "ColumnDefsPreferences": true
      }
  },
  },
  "search": {
      "savedSearches":["searchName"]
    }
}

I am expecting this result to be saved in the user's document

{
  "preferences": {
    "settings": {
      "themeColorMode": "dark-mode",
      "isSoundNotificationOn": false,
      "isAppListeningToStream": true,
      "debugMode": false
    },
    "filterChips": {
      "pathName": {
        "filterChipsPreferences": false
      },
      "FirstUsedSearch": "23/12/2021"
    },
    "columnDefs": {
      "pathName": {
        "ColumnDefsPreferences": true
      }
  },
  "search": {
      "savedSearches":["searchName"]
    }
  }
}

How could I approach that?


Solution

  • Since you are using the Node.js Admin SDK on the server that calls Firestore, it is actually quite straightforward: you need to use the update() method of the DocumentReference.

    The update() method accepts either an object with field paths encoded as keys and field values encoded as values, or a variable number of arguments that alternate between field paths and field values.

    More precisely:

    // 1/ Define your DocumentReference
    const userId  = ...;
    const docRef = admin.firestore().doc(`users/${userId}`);
    
    // 2/ Get the desired elements from the payload received by your API and use them to build the object to pass to the update method
    
    // For example
    
    const updateObj = {
        preferences.settings.debugMode: false,
        preferences.settings.themeColorMode: "myColor",
        preferences.filterChips.filterChipsPreferences: "myPref",
        aNewRootNode: {
           foo: 'bar',
           bar: 'foo'
        }
    }
    
    await docRef.update(updateObj);
    

    More info on how to update fields in nested objects can be found in the doc of the JS SDK.