182

I came across a piece of Mongoose code that included a query findOne and then an exec() function.

Ive never seen that method in Javascript before? What does it do exactly?

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
user439133
  • 1,943
  • 3
  • 12
  • 5

8 Answers8

230

Basically when using mongoose, documents can be retrieved using helpers. Every model method that accepts query conditions can be executed by means of a callback or the exec method.

callback:

User.findOne({ name: 'daniel' }, function (err, user) {
  //
});

exec:

User
  .findOne({ name: 'daniel' })
  .exec(function (err, user) {
      //
  });

Therefore when you don't pass a callback you can build a query and eventually execute it.

You can find additional info in the mongoose docs.

UPDATE

Something to note when using Promises in combination with Mongoose async operations is that Mongoose queries are not Promises. Queries do return a thenable, but if you need a real Promise you should use the exec method. More information can be found here.

During the update I noticed I didn't explicitly answer the question:

Ive never seen that method in Javascript before? What does it do exactly?

Well it's not a native JavaScript method, but part of the Mongoose API.

danillouz
  • 6,121
  • 3
  • 17
  • 27
  • 4
    What if I have set mongoose.Promise = require('bluebird'), do I still need to use .exec() ? Thanks. – wayofthefuture Jan 07 '17 at 23:29
  • 4
    @wayofthefuture I find the docs really confusing about this matter, but I believe you still need to call the `exec` method. This is what they do in the docs at least. To be sure you can check yourself with `Model.find() instanceof require('bluebird')`. Hope this helps. – danillouz Jan 08 '17 at 17:31
  • 1
    And then there is the question of other operations such as delete and insertMany... would exec() be necessary in these cases? Delete would have to use some kind of query underneath the hood... – wayofthefuture Jan 10 '17 at 20:56
  • 1
    exec also returns a promise, if no callback is passed, which is very convenient I must say – Alexander Mills Mar 07 '17 at 20:14
  • *It might save someone*. If you think that Model.update() will do its job and don't wait for some not-so-important data to be updated and return response to the API; Your update won't execute. You need to `Model.update().exec()` to make sure it will execute. Then you can respond to the api without waiting for update. – Yogesh Feb 20 '20 at 11:36
  • Is it possible to do a .find({}) query and then execute it later? Like `const query = await Model.find({});` and then later `query.exec();` ? – Matthew Wolman Mar 13 '20 at 20:03
110

I never use exec() function to complete a CRUD(Create, Read, Update, Delete) on a model. When I want CRUD on a model, I use it like this:

const user = await UserModel.findOne(userCondition);

And it always does the job. So I wondered "what does exec() use for"? As I searched in mongoose document, I found the answer here.

Should You Use exec() With await?

And here is the story.
You have two ways to execute a query on a model. Using callback or using exec() function. "But" you can use await too. exec() function returns a promise, that you can use it with then() or async/await to execute a query on a model "asynchronous". So the question is "If I can just use user = await UserModel.find() and it works currectly, so why should I use exec() function?". The answer that you can find in the document is:

There are two difference between using await with exec() or without it.

  • As a functionality point of view, there is no difference between using await with exec() or without it. Just when you call a query without exec() or callback, it returns a thenable which is something like promise but it's not a promise.(You can find the difference here). But when you use exec() to run a query, you get exactly a promise as response.
// returns a thenable as response that is not a promise, but you can use await and then() with it.
const user = await UserModel.findOne(userCondition);

// returns exactly a promise.
const user = await UserModel.findOne(userCondition).exec(); 
  • Another difference is, if you use await with exec() you get a better "stack trace" if you catch any error in executing queries. So:

    These two line, do the same thing:
const user = await UserModel.findOne(userCondition);

// does exactly as the before line does, but you get a better stack trace if any error happened
const user = await UserModel.findOne(userCondition).exec(); 
farid
  • 1,153
  • 1
  • 5
  • 8
  • 1
    Very important difference! When testing my queries I noticed that when I distorted on purpose the format of _id field in the url to test for error, the try / catch was not picking the error and my models was issuing the deprecated warning, that promises that return errors that are not catched won't be accommodated any more in the future. – Julio Spinelli Oct 20 '21 at 12:44
  • 2
    "As far as functionality is concerned, these two are equivalent. However, we recommend using .exec() because that gives you better stack traces." [Direct docs ref](https://mongoosejs.com/docs/promises.html#should-you-use-exec-with-await). – CodeFinity Jan 01 '23 at 03:28
  • is this recommended with all of the await mongoose operations? I mean await user.deleteOne() too. – Luis May 19 '23 at 15:52
80

Daniel has answered this quite beautifully. To elaborate on an exhaustive list of ways to build and execute queries, look at the following use cases:

Query Building

Mongoose will not execute a query until then or exec has been called upon it. This is very useful when building complex queries. Some examples can include using the populate and aggregate functions.

User.find({name: 'John'}) // Will not execute

Execution via callback

Although disliked by many due to its nesting nature, queries can be executed by providing the optional callback.

User.find({name: 'John'}, (err, res) => {}) // Will execute

Then API as a Promises/A+

Mongoose queries do provide a then function. This is not to be confused with regular promises. Simply put, the Promises/A+ specification requires a then function to work much like how we're used to with promises.

User.find({name: 'John'}).then(); // Will execute
Promise.all([User.find({name: 'John'}), User.find({name: 'Bob'})]) // Will execute all queries in parallel

The exec function

From Mongoose docs If you need a fully-fledged promise, use the .exec() function.

User.find({name: 'John'}).exec(); // Will execute returning a promise
Anshuul Kai
  • 3,876
  • 2
  • 27
  • 48
  • 1
    the docs didn't mention it explicitly but the examples show User.save() returning a promise as well. Apart from exec() and save() do any other mongoose methods on the model return a promise, or is it just these two? – thetrystero Apr 03 '17 at 12:04
  • As pointed above, you can use `then` on a query to return a promise too. This isn't much different than `exec`. The use case I find this handy is when using something like `Promise.all`. Not sure if the promise returned by `exec` works in such contexts though. – Anshuul Kai Apr 05 '17 at 15:36
  • Can you please add a complete example with .exec() ? @AnshulKoka Is it that the .exec() automatically makes the query asynchronous or should i add async/await – O'Dane Brissett Sep 20 '19 at 15:48
  • Making the query asynchronous will require async/await so I'm not sure if I understand the question. Just prefix await to my example for your request for a complete example. – Anshuul Kai Sep 21 '19 at 17:07
  • See here for explanation : https://stackoverflow.com/a/46457247/3904109 – DragonFire Feb 20 '21 at 03:23
  • The `Promise.all` example is probably too simplistic, as you should use `find(name: { $in: ['John', 'Bob'] })` instead – OneCricketeer Jan 19 '23 at 21:32
11

exec() will return a promise if no callback is provided. So the following pattern is very convenient and generic - it can handle callbacks or promises nicely:

function findAll(query, populate, cb) {

  let q = Response.find(query);

  if (populate && populate.length > 0) {
    q = q.populate(populate);
  }

  // cb is optional, will return promise if cb == null
  return q.lean().exec(cb);

}

I recommend using Bluebird promises with Mongoose, to do that, use this call:

const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
3

all answers are correct but the easiest way is to use modern async await approach..

async ()=> {
const requiresUser = await User.findByIdAndUpdate(userId,{name:'noname'},{ new:true}).exec()
Julio Spinelli
  • 587
  • 3
  • 16
shubham
  • 367
  • 6
  • 14
1

One way to get data:

find().exec((err,data)=>{

})

Other way:

const res=await find()
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 08 '22 at 06:58
1

Basically, mongoose queries do not return any promise. So that if we want them to queries work like a promise we use the exec function.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Albert Logic Einstein Aug 23 '22 at 14:45
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/32522514) – Webdeveloper_Jelle Aug 24 '22 at 13:32
0

2023 update

Query.prototype.exec() no longer accepts a callback

Use following pattern:

const result = await Model.findOne({filter:filter}).exec();
return result ;

**or**

const x = await Model.findOne({filter:filter}).exec()
.then((data)=>{
   return (data);
}).catch((err)=> {
     return (err);
});
vahid sabet
  • 485
  • 1
  • 6
  • 16