MongoDB Remote Access - iOS SDK insert

I am working with MongoDB Remote Access - iOS SDK to retrieve and (attempt to) insert documents.

At the moment when we authenticate using the standard MongoDB Realm authentication

let creds = Credentials.emailPassword(email: username, password: password)
app.login(credentials: creds) { result in

we can retrieve data using the remote access functions like this

let client = app.currentUser!.mongoClient("some-client-db-atlas")
let database = client.database(named: "my-database")
let collection = database.collection(withName: "MyCoolCollection")
let whichPartition = "CoolPartition"
collection.find(filter: ["_partitionKey": AnyBSON(whichPartition)], { (result) in

And the data is fetched and returns correctly.

However, inserting/writing isn’t working

let myData = [
    ("key0", AnyBSON(stringLiteral: "value0")),
    ("key1", AnyBSON(stringLiteral: "value1"))
]
let myDoc = Document(uniqueKeysWithValues: myData)
collection.insertOne([myDoc], { results in
    print(results)
})

results in an error

failure(Error Domain=realm::app::ServiceError Code=-1 “insert not permitted”

It’s possible (?) that we need to create a separate user in the Realm Console Atlas->Database Access and Network Access sections based on this post and then try a separate login/auth

let username = "UserA"
let password = "kaijas09ajadjjad"
let creds = Credentials.emailPassword(email: username, password: password)
app.login(credentials: creds, { result in
    print(result)
})

But we get an invalid email/password error.

I see that inserting is not support on Data Lake but that’s not applicable to our situation.

What’s the correct process to make this work so we can insert?

Hey Jay!

That “insert not permitted” error most likely means that your server-side rules aren’t set up to allow the write. I’d start by checking your rules to make sure they’re defined correctly.

Note that you won’t need to create db users in Atlas for anything in Realm - those users are totally separate from the email/password auth provider. Realm creates db users for each app and uses them behind the scenes to handle your app’s requests.

1 Like

Thank you @nlarew

This is an existing MongoDB Realm user that has full read/write privileges and can read and write to our App’s database collection successfully.

Again, we can read via Remote Access, but it only fails when attempting a write.

There are no Rules defined for the app either; Sync Permissions for read and write are both set to true.

Any suggestions as where to look?

Hi @Jay, you mention that there are no Rules defined for the Realm app – you should have some set up so to enable Realm users to read/write this collection.

Are you also seeing errors in the Realm logs?

@Andrew_Morgan

There are no rules in the console: Realm->App->Rules. But again, the user can read and write with no issues from the SDK. They can read this using Remote Access but not write.

There is an error but the message isn’t really clear.

Error:
insert not permitted : could not validate document: (root): _id is required
Stack Trace:

FunctionError: insert not permitted at <eval>:16:4(4)
Details:
{
  "serviceAction": "insertOne",
  "serviceName": "mongodb-atlas",
  "serviceType": "mongodb-atlas"
}
{
  "arguments": [
    {
      "database": "my-database",
      "collection": "TaskClass",
      "document": {
        "status": "Open",
        "name": "Inserted Task",
        "_partitionKey": "Task Tracker"
      }
    }
  ],
  "name": "insertOne",
  "service": "mongodb-atlas"
} 

We are using insertOne, which, according to the docs, if _id is missing, one will be generated and returned. Here’s the object construction in Swift

let taskName = AnyBSON(stringLiteral: "Inserted Task")
let status = AnyBSON(stringLiteral: "Open")
let partition = AnyBSON(stringLiteral: Constants.REALM_PARTITION_VALUE)
let taskDict = ("name", taskName )
let statusDict = ("status", status)
let partitionDict = ("_partitionKey", partition)
let myTaskDoc = Document(dictionaryLiteral: taskDict, statusDict, partitionDict)
collection.insertOne(myTaskDoc, { result in
   print(result)
})

I confess that I haven’t yet worked with this feature of the Realm SDK, but it could well be that its behavior doesn’t exactly match the documented Swift driver behavior as it passes through the Realm service rather than connecting directly to Atlas.

Have you tried adding the _id attribute to confirm if that’s the issue?

Is there a schema defined in the Realm app for this collection?

@Andrew_Morgan

And there we have it - success. Here’s the low down for future readers

    //your Realm App Id. Console->Realm->Click app, AppID at top
    let app = App(id: "xxxx-yyyy") 

    //this is the default name. aka the same name as the Service Name or  Linked Cluster name
    //  found in Console->Realm->click your app->Linked Data Sources in left column
    let client = app.currentUser!.mongoClient("mongodb-atlas") 

    //found in Console->Realm->Click app, AppID->Schema in left column
    let database = client.database(named: "track-tracker-database") 

    //also found in Schema section for whatever database you're using
    let collection = database.collection(withName: "TaskClass") 

    //the data to be written must be in a <String, AnyBSON> format so here are the values
    //  we're going to write
    let _id = AnyBSON(ObjectId.generate()) //generates the required objectID for the objects
    let taskName = AnyBSON(stringLiteral: "Inserted Task") //a value for your objects properties
    let status = AnyBSON(stringLiteral: "Open") //another value for your objects properties
    let partition = AnyBSON(stringLiteral: "my_partiton") //whatever partition (realm) you want the object to be inserted into

    //these are the keys and associated values to store with the object in a <String, AnyBSON> format
    let idDict = ("_id", _id)
    let taskDict = ("name", taskName )
    let statusDict = ("status", status)
    let partitionDict = ("_partitionKey", partition)

    //note that inserting data **requires** a populated Document object
    let myTaskDoc = Document(dictionaryLiteral: idDict, taskDict, statusDict, partitionDict)

    collection.insertOne(myTaskDoc, { result in
        print(result)
    }) 

See Document and insertOne for more reading

The .insertOne print’s the following to console indicating success

success(RealmSwift.AnyBSON.objectId(609195b56312471d6b083fe6))

2 Likes