1
votes

When trying to add new item (request) for a collection in my Firestore, Although The item is properly inserted to the collection - I'm getting an error that I can't figure.

updated

Unhandled error RangeError: Maximum call stack size exceeded at isLength (/srv/node_modules/lodash/lodash.js:11739:22) at isArrayLike (/srv/node_modules/lodash/lodash.js:11359:31) at keys (/srv/node_modules/lodash/lodash.js:13333:14) at /srv/node_modules/lodash/lodash.js:4920:21 at baseForOwn (/srv/node_modules/lodash/lodash.js:2990:24) at Function.mapValues (/srv/node_modules/lodash/lodash.js:13426:7) at encode (/srv/node_modules/firebase-functions/lib/providers/https.js:184:18) at /srv/node_modules/lodash/lodash.js:13427:38 at /srv/node_modules/lodash/lodash.js:4925:15 at baseForOwn (/srv/node_modules/lodash/lodash.js:2990:24)

HTML

<!-- new request modal -->
  <div class="new-request">
    <div class="modal">
      <h2>New request</h2>
      <form>
        <input type="text" name="request" placeholder="request...">
        <button>Add</button>
        <p class="error"></p>
      </form>
    </div>
  </div>

app.js

// add a new request
const requestForm = document.querySelector(".new-request form");
const requestModal = document.querySelector(".new-request");
requestForm.addEventListener("submit", (e) => {
  e.preventDefault();

  const addRequest = firebase.functions().httpsCallable("addRequest");
  addRequest({
    text: requestForm.request.value,
  })
  .then(() => {
    requestForm.reset();
    requestModal.classList.remove("open");
    requestForm.querySelector(".error").textContent = "";
  })
  .catch((error) => {
    requestForm.querySelector(".error").textContent = error.message;
  });
});

index.js (firebase functions) updated*

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// auth trigger (user sign up)
exports.newUserSignup = functions.auth.user().onCreate((user) => {
  // for background triggers you must return a value/promise
  return admin.firestore().collection("users").doc(user.uid).set({
    email: user.email,
    upvotedOn: [],
  });
});

// auth trigger (user deleted)
exports.userDeleted = functions.auth.user().onDelete((user) => {
  // for background triggers you must return a value/promise
  const doc = admin.firestore().collection("users").doc(user.uid);
  return doc.delete();
});

// http callable function (adding a request)
exports.addRequest = functions.https.onCall((data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError(
      "unauthenticated",
      "only authenticated users can add requests"
    );
  }
  if (data.text.length > 30) {
    throw new functions.https.HttpsError(
      "invalid-argument",
      "request must be no more than 30 characters long"
    );
  }
  return admin.firestore().collection("requests").add({
    text: data.text,
    upvotes: 0,
  });
});
1
I tried your code and it works perfectly, I cannot reproduce. Is it a client-side or server-side error? - Louis Coulet
@LouisCoulet error from chrome console: service.ts:160 POST us-central1-requests-demo-74dd6.cloudfunctions.net/addRequest 500 - Moshe
As said, it must be something outside of the code that you have posted. Do you see somethink in Firebase console > Functions > Logs ? - Louis Coulet
@LouisCoulet Yes, Unhandled error RangeError: Maximum call stack size exceeded - Moshe
Great, that narrows the issue to your index.js file. Have you copied its entire content here? Also, nothing else to point at the callstack in the logs? - Louis Coulet

1 Answers

2
votes

This issue is similar to this one: https://stackoverflow.com/a/52569728/6016470
You should not directly return the Promise<DocumentReference> produced by your call to firestore as the result of your Cloud Function. This is because a DocumentReference is not intended for being sent back as it is not serializable (your error message comes from lodash failing to serialize due to circular references).

So in order to send serializable result, you should modify your functions as such:

exports.addRequest = functions.https.onCall((data, context) => {
  // ... 
  return admin.firestore().collection("requests").add({
    text: data.text,
    upvotes: 0,
  }).then(doc => {
    return doc.id;
  });
});

This way, your Cloud Function now returns a Promise<string>, so it can send back its result to your client.