Error in collection.update call from client in version 3

Hi, attempting to migrate some packages to version 3, I encountered this problem. As the issue opening procedure is not clear to me, I write about the problems here.

The allow-deny package does not allow the update of a document from the client using the non-ascricronous version ‘update’. Looking at the source, it can be seen that while the distinction between synchronous and asynchronous has been handled for insertion, the server-side synchronous methods are still called for update. Specifically by invoking from the client:

collection.update({_id: id}, {$set: {a=1}}, fn);

I get the server-side error

Exception while invoking method '/links/update' TypeError: self._collection.findOne is not a function

@denyhs

I think in version 3, you must use async api. collection.updateAsync

@minhna This decision was made some time ago. If I have not missed anything, the client should guarantee both asynchronous and synchronous functionality. Otherwise, the changes made to the allow-deny package would make no sense.

Then there is also the problem that in the asynchronous version it is still not possible to intercept any error generated on the server side.

You said the error was on server side. The server side doesn’t support sync version since version 3.

You can use try/catch.

You said the error was on server side. The server side doesn’t support sync version since version 3.

Sure, but if you look at the modification made by @denyhs for the insert, it solves this (validateInsert). Why can’t update and remove?

You can use try/catch .

The try/catch does not solve it, we are talking about intercepting the server response which is still not possible in Asynchronous. As you know, CRUD calls on remote collections are translated into methods that do not wait for the server’s response. There are still discussions going on

Can you share a snippet of the issue you had?

To isolate the problem, I created a new app with the command

meteor create --release 3.0-alpha.11 test

Then I made the LinksCollection global in client\main.js so I could manage it from the console.

The insert works correctly:

LinksCollection.insert({
title: 'Do the Tutorial',
url: 'https://www.meteor.com/tutorials/react/creating-an-app',
},console.log)

While the update for _id:

LinksCollection.update({_id: 'obhFXzDYWh6rNvNoT'},{$set: {title: 'Do the Tutorial 2'}},console.log)

Produces the error:

Exception while invoking method '/links/update' TypeError: self._collection.findOne is not a function

The error occurs in line 475 of allow-deny.js. Synchronous methods should be revised to asynchronous ones:

const doc = self._collection.findOne(selector, findOptions);
if (!doc) // none satisfied!
return 0;

in the validateInsert this has been handled:

return (Meteor.isServer
? self._collection.insertAsync
: self._collection.insert
).call(self._collection, doc);

I assume you can do the same to avoid incompatibility.

Thank you.

As I understand, you’re trying to write a code which will run on both client/server.
I could be wrong, but I think the problem is, in Meteor v3, the server side only supports async apis while the client (I’m not sure about this) supports both async and sync apis.
So in order to make your code work on both client and server, you need to use async version.

@minhna, the code only runs on the client side and uses synchronous methods.

do you mean “meteor insecure package”?

By synchronous I mean the type call:

LinksCollection.update({_id: 'obhFXzDYWh6rNvNoT'},{$set: {title: 'Do the Tutorial 2'}},console.log)

what is your list of meteor packages?

As indicate in my previous post, I created a new app to isolate the problem:

ecmascript              1.0.0-alpha300.11  Compiler plugin that supports ES2015+ in all .js files
es5-shim                5.0.0-alpha300.11  Shims and polyfills to improve ECMAScript 5 support
hot-module-replacement  1.0.0-alpha300.11  Update code in development without reloading the page
meteor-base             2.0.0-alpha300.11* Packages that every Meteor app needs
mobile-experience       2.0.0-alpha300.11  Packages for a great mobile user experience
mongo                   2.0.0-alpha300.11  Adaptor for using MongoDB and Minimongo over DDP
react-meteor-data       3.0.0-alpha300.6  React hook for reactively tracking Meteor data
reactive-var            2.0.0-alpha300.11  Reactive variable
shell-server            1.0.0-alpha300.11  Server-side component of the `meteor shell` command.
standard-minifier-css   2.0.0-alpha300.11  Standard css minifier used with Meteor apps by default.
standard-minifier-js    3.0.0-alpha300.11  Standard javascript minifiers used with Meteor apps by default.
static-html             2.0.0-alpha300.11  Define static page content in .html files
typescript              5.0.0-alpha300.11  Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files
```

is your collection update call: LinksCollection.update inside a Meteor.methods? And you call that method by calling Meteor.call or Meteor.callAsync?

Why not share the app in GitHub so folks can test it.

The update not within a method call. If you try to create a new application like:

meteor create --release 3.0-alpha.11 test

and try to update a record in the LinksCollection from the client with a normal update:

LinksCollection.update({_id: ''},{$set: {title: 'Do the Tutorial 2'}},console.log)

you get the error:

Exception while invoking method '/links/update' TypeError: self._collection.findOne is not a function

I mention methods because, for remote collections, Meteor automatically generates methods to manage the collection itself. For example, when invoking <collection>.update from the client, Meteor first performs the update on the client’s local collection and then invokes the update method.

It seems to me a waste of space as all you have to do is create a new application in any form and nothing else.

I am returning to the subject for a discussion with you to see how we should approach the migration of packages to version 3.0. From an initial survey, the idea would be to support version 3 and earlier versions so that the packages are backward compatible. However, I find some difficulties in doing this that I would like to share with you:

  • I have not found a way to use a common syntax when invoking the insert, update and remove methods from client-side in the case of remote collections. The behaviour in the two versions is completely different. If we are to be backwards compatible, the behaviour should be the same.

  • in version 3 collection.allow accepts as attribute of the passed object the new functions: insertAsync, updateAsync and removeAsync. In version 2.13.3, which already supports async calls, the presence of the above functions throws an exception: Invalid key. This is because the regular expression is different in the two versions even though they support the same methods.

  • Even the calling of remote methods, Meteor.call, does not have the same behaviour. In this case the call to methods with ‘call’ in one case returns a promise and in the other a value. There is a long discussion on the subject, but it is not clear to me what the future holds and whether it is still worthwhile to assume the use of Meteor.call on the client side.

I realise that we are still in alpha version and everything could change, but since Hacktoberfest is starting soon, it would not be bad to share a guideline in the conversion of community packages.

Has anyone else already experienced these migration issues?

Thank you

2 Likes

To complete my previous thread I add an example that works in Meteor 2.13.3 but not in 3-alpha-15

  if (Meteor.isClient) {
        it('manage collection', function (done) {
            LinksCollection.insert({ title: 'test 1', url: 'test 1' }, function(err, res) {
                if(err) {
                    done(err)
                } else {
                    LinksCollection.update({ _id: res }, {$set: { title: 'test 1', url: 'test 1' }}, function (err, res) {
                        console.log('updated',res);
                        if(err) {
                            done(err)
                        } else {
                            console.log('updated',res);
                            done();
                        }
                    })
                }
            });
        });
 }

As indicated above, the alpha produces the server-side error:

'/links/update' TypeError: self._collection.findOne is not a function.

and callbak is not invoked.

Hi @grubba, can anyone tell me if this is a issue or how we should remedy it. We are migrating packages to version 3.0 trying to ensure backwards compatibility and support for non-asynchronous mongo calls on remote collections. This mode is used in packages such as collection2 and collection_hooks. In my opinion, the problem should be solved on the server side since synchronous calls no longer exist while on the client side they are still supported.

Hey @pbeato! Thanks for reporting that error @denyhs was able to solve it(PR)

Now, it will work as expected. You can still use those sync methods in the client.

1 Like