MongoDB Cloud Function returning double-stringified JSON string

Hi there,

I’m having quite the time figuring out how to include error codes in the responses from my Realm functions. A simple string message with name “Error” doesn’t meet my requirements. Unfortunately, the realm function will return a different format based on how it is used.

Correctly…

When I catch an error from a promise, and rethrow:

    await new Promise((res, rej) => {
        setTimeout(() => {
            rej(JSON.stringify({message: "An error", code: 42}))
        }, 300)
    }).catch(err => {
        throw err
    })

and catch it in the client (.js in this case)

await user.functions.errorTest().catch(errResponse => {
    console.log(errResponse)
})

the errResponse.error property is properly stringified:

{"code":42,"message":"An error"}

I believe this is the correct behaviour for the realm functions, and is what I expect to happen.


Unfortunately

From another realm function, when I throw a stringified javascript object:

throw JSON.stringify({message: "an error message", code: 42})

the response is double-stringified JSON:

"{\"code\":42,\"message\":\"An error\"}"

And the only way I can think of working with it is to parse it twice. My client is in swift so this is introducing instability and try catch expressions - which doesn’t make sense.

The reason I can’t return like so:

throw {message: "an error message", code: 42}

is because it will be stringified by the system as EJSON which I cannot work with. Also, why should I have to? I think this is a bug.

Hello can someone please approve this and make it visible? Really could use some help here.

Hopefully, someone can suggest something more elegant, but this works…

Realm function (gotAProblem):

exports = function(){
  throw JSON.stringify({message: "an error message", code: 42});
};

Swift code using Realm-Cocoa:

struct MyError: Decodable {
    let message: String
    let code: Int
    
}

private func errorProne() {
    app.currentUser!.functions.gotAProblem([]) { (_, error) in
        if let error = error {
            let myError = try! JSONDecoder().decode(MyError.self, from: error.localizedDescription.data(using: .utf8)!)
            print("Error: \(myError.message)")
        }
    }
}

Your code works, however my realm function is declared async and that appears to cause my issue. Although I don’t understand the language well enough to explain why that makes sense, it does in the context of my current solution, which is to return a Promise for every async function I need to write, that throw the JSON.stringified error object from inside their final catch block. I leave an example of what works in case it helps anyone save the time I had to put in experimenting with this.

Please add documentation about this to you realm functions page!

exports = async (assetId) => {
    const db = context.services.get("mongodb-atlas").db("realm-sync")
    const userId = context.user.id

    let assetIdentity = await AssetIdentities.findOne({ _id: BSON.ObjectId(assetId) })

    return new Promise(async (resolve, reject) => {
        ///
        /// Guard conditions
        ///
        const assetListing = await AssetListings.findOne({
            _id: BSON.ObjectId(assetId)
        })

        if (!assetListing) {
            return reject(JSON.stringify({
                message: `The asset with id ${assetId} does not exist`,
                code: 404
            }))
        }

        if (assetListing.owner_id !== userId) {
            return reject(JSON.stringify({
                message: "An asset can only be destroyed by the asset's owner",
                code: 405
            }))
        }

        ///
        /// Post-guard execution
        ///

        /// Delete Asset Identity
        await AssetIdentities.deleteOne({
            _id: BSON.ObjectId(assetId),
            _partition: `/${userId}/assets`
        })

        /// Delete Asset Listing
        await db.collection("AssetListing").deleteOne({
            _id: BSON.ObjectId(assetId)
        })

        /// Made it
        resolve()
    }).catch(err => {
        /// Send correctly formatted error object
        /// inside catch block to avoid the client receiving
        /// "{\"code\":42,\"message\":\"An error\"}"
        throw err
    })
}

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.