Problem sending POST body to the Firestore REST API

Viewed 895

I want to create a new document in Firestore using the REST API.

Very good examples here using Axios to send the POST request with some fields:
https://www.jeansnyman.com/posts/google-firestore-rest-api-examples/

axios.post(
"https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>", 
    { 
        fields: { 
            title: { stringValue: this.title }, 
            category: { stringValue: this.category }, 
            post: { stringValue: this.post }, 
            summary: { stringValue: this.description }, 
            published: { booleanValue: this.published }, 
            created: { timestampValue: new Date() }, 
            modified: { timestampValue: new Date() } 
        } 
    }
    ).then(res => { console.log("Post created") })

And an example here using Python Requests:
Using the Firestore REST API to Update a Document Field
(this is a PATCH request but the field formatting is the same as in a POST request)

import requests
import json

endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"

body = {
    "fields" : {
        "[FIELD_1]" : {
            "stringValue" : "random new value"
        }
    }
}

data = json.dumps(body)

headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data,  headers=headers).json())

I am using Google Apps Script UrlFetchApp.fetch to send my requests. I am able to use GET requests with no problems. For example, to get all the documents in a collection (in Google Apps Script):

function firestore_get_documents(){
  var options = {
    headers: { 'Authorization': 'Bearer ' +  ScriptApp.getOAuthToken() },
    method:'GET'
  }
  var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
  var parsed = JSON.parse(response.getContentText());
  return parsed;
}

This works nicely. And changing 'method' to 'POST' creates a new document in myCollection as expected. Then I try to add a POST body with some fields (or just one field):

function firestore_create_new_document(){
  
  var options = {
    headers: { 'Authorization': 'Bearer ' +  ScriptApp.getOAuthToken() },
    method:'POST',
    payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
    muteHttpExceptions:true
  }
  var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
  var contentText = response.getContentText();
  var parsed = JSON.parse(response.getContentText());
  return parsed;
}

I get the following errors:

code: 400 message: "Request contains an invalid argument."
status: "INVALID_ARGUMENT"
details[0][@type]: "type.googleapis.com/google.rpc.BadRequest"
details[0][fieldViolations][0][field]: "{title={stringValue=newTitle}}"
details[0][fieldViolations][0][description]: "Error expanding 'fields' parameter. Cannot find matching fields for path '{title={stringValue=newTitle}}'."

Documentation is available here:
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document

  1. The problem may be the formatting of my 'fields' object - I've tried several different formats from the documentation and examples
  2. The problem may be that the fields don't exist yet? I think I should be able to create a new document with new fields
  3. The problem may be with the way UrlFetchApp.fetch sends my JSON body. I have tried using payload = JSON.stringify(payload_object) and that doesn't work either.

I think UrlFetchApp is doing something slightly different than Axios or Python Requests - the body is getting sent differently, and not parsing as expected.

1 Answers

How about the following modification?

From:

var options = {
  headers: { 'Authorization': 'Bearer ' +  ScriptApp.getOAuthToken() },
  method:'POST',
  payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
  muteHttpExceptions:true
}

To:

var options = {
  headers: { 'Authorization': 'Bearer ' +  ScriptApp.getOAuthToken() },
  method:'POST',
  payload: JSON.stringify({fields: { title: { stringValue: 'newTitle' } } }),
  contentType: "application/json",
  muteHttpExceptions:true
}
  • When I tested above modified request, I could confirm that it worked. But if other error occurs, please tell me.

Reference:

Related