Search code examples
javascriptparse-serverparse-cloud-code

ParseError when using Nested Cloud Code functions with Promises


I need help as I'm a bit new to Promises and working with Cloud Code at the moment. I've checked other questions here on stackoverflow but they seem to be all outdated using the deprecated Parse.Promise class.

Note: I am working on a locally installed parse-server to be able to test my Cloud Code easier.

I have 2 functions.

Parse.Cloud.define("getTag", async (request) => {}
Parse.Cloud.define("createTag", async (request) => {}

Basically, I call getTag passing a String. The function checks if the tag exists and then returns it. if not, it creates the tag with by calling the createTag function and then returns the new Tag object.

either way, I should be getting a Tag object.

I have the getTag function working fine and returning existing objects. But I'm stuck and CAN'T GET the newly created Tag object from the createTag function to pass through / return to getTag function.

Even though the Tag is created properly and ends up in the Tag Class as expected when I check the database, I get this error:

ParseError: The server returned an invalid response.
    at <path>/node_modules/parse-server/node_modules/parse/lib/node/Cloud.js:163:13
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
  code: 107
}

Basically I'm not sure if I'm returning the object from createTag correctly.

Here are my 2 functions. Any help would be appreciated.

Parse.Cloud.define("getTag", async (request) => {
    console.log("===== Begin getTag ====== \n\n\n")

    const Tag = Parse.Object.extend("Tag");
    const query = new Parse.Query(Tag);

    query.equalTo("tag", request.params.tag);
    await query.first().then(
        function(foundTag) {
            // Check if we got an object back.
            if (foundTag === undefined || foundTag === null) {
                // We have to create a new one.
                console.log("NO TAG FOUND:\nLet's create a new tag with - ", request.params.tag)    
                Parse.Cloud.run("createTag", {tag: request.params.tag}).then(function(createdTag) {

// PROBLEM IS HERE - I CAN'T get createdTag object.

                    return createdTag;
                })
                .catch(function(error) {
                    // There was an error.
                    console.log(error)
                    return error
                });
            } else {
                // We found an existing Tag Object
                console.log("Found an EXISTING TAG:", foundTag);
                console.log("\n\n\n")

                return foundTag;        
            }
        }).catch(function(error) {
            // There was an error.
            console.log(error)
            return error
        });
});

Parse.Cloud.define("createTag", async (request) => {
    console.log("n\n ===== Begin createTag ====== \n\n\n")

    var tagString = request.params.tag
    const TagClass = Parse.Object.extend("Tag");
    const Tag = new TagClass();
    Tag.set("tag", tagString);
    const results = await Tag.save().then(function(newTag) {
        // Execute any logic that should take place after the object is saved.
        console.log("TAG CREATED:", newTag);
        console.log("\n\n\n")
/* 
THIS WORKS FINE. newTag object prints out
TAG CREATED: ParseObjectSubclass {
  className: 'Tag',
  _objCount: 3,
  id: 'DOaiQGuzLB'
}
*/
        return newTag;
    })
    .catch(function(error) {
        // There was an error.
        console.log(error)
        return error
    });
});

This is the line in Cloud.js 163

  throw new _ParseError.default(_ParseError.default.INVALID_JSON, 'The server returned an invalid response.');

Solution

  • I finally figured it out and it's a sad one.

    It all boils down to me not understanding the structure of how .then and async functions worked. I assumed the return inside a .then() meant return of the overall function. When I figured out that it wasn't, chaining up the return function was the solution.

    My Parse.cloud.run("createTag") function itself didn't have a return object and that's why it was returning undefined up to the getTag function.

    The solution was simply adding "return results" at the end of the createTag Function.

    Parse.Cloud.define("createTag", async (request) => {
        console.log("n\n ===== Begin createTag ====== \n\n\n")
    
        var tagString = request.params.tag
        const TagClass = Parse.Object.extend("Tag");
        const Tag = new TagClass();
        Tag.set("tag", tagString);
        const results = await Tag.save().then(function(newTag) {
            // Execute any logic that should take place after the object is saved.
            console.log("TAG CREATED:", newTag);
            console.log("\n\n\n")
    
            return newTag;
        })
        .catch(function(error) {
            // There was an error.
            console.log(error)
            return error
        });
    
    // ADDED THIS
    return results
    });
    

    At least now I have a better understanding of how async and promises work. I hope this saves other newbie developers like myself some time in the future.