{"id":396,"date":"2015-09-18T23:25:16","date_gmt":"2015-09-18T21:25:16","guid":{"rendered":"http:\/\/joost.vunderink.net\/blog\/?p=396"},"modified":"2015-10-12T16:11:08","modified_gmt":"2015-10-12T14:11:08","slug":"combining-mongoose-and-q-in-node-js","status":"publish","type":"post","link":"https:\/\/joost.vunderink.net\/blog\/2015\/09\/18\/combining-mongoose-and-q-in-node-js\/","title":{"rendered":"Combining mongoose and Q in node.js"},"content":{"rendered":"<p>This post will teach you how to write <a href=\"https:\/\/en.wikipedia.org\/wiki\/Futures_and_promises\">promise-based<\/a> <a href=\"http:\/\/mongoosejs.com\/\">mongoose<\/a> code, using <a href=\"https:\/\/github.com\/kriskowal\/q\">Kris Kowal&#8217;s Q library<\/a>.<\/p>\n<p>You can also find the code below in the `src\/mongoose-and-q` dir of my <a href=\"https:\/\/github.com\/joostvunderink\/jvblog-node-samples\">blog code repository<\/a>.<\/p>\n<p>We\u00a0have a mongo database with 3 collections: users, bicycles and cars. Bicycles and cars are owned by users. We\u00a0will write a script that shows the bicycles and cars owned by\u00a0<span class=\"lang:default decode:true crayon-inline \">rupert@example.com<\/span>\u00a0.<\/p>\n<p>We&#8217;ll look at multiple\u00a0versions of this script. This article\u00a0focuses on callbacks and promises, so we&#8217;ll be using a hardcoded email address and we&#8217;ll not be handling errors very well. Those aspects of the code are for a different article.<\/p>\n<p>The first version uses callbacks for the database calls.<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\" title=\"The first version, with callbacks\">'use strict';\r\n\r\nvar mongoose = require('mongoose');\r\nvar Schema   = mongoose.Schema;\r\n\r\nvar UserSchema = new Schema({\r\n  email    : { type: String },\r\n  firstName: { type: String },\r\n});\r\nvar User = mongoose.model('User', UserSchema);\r\n\r\nvar CarSchema = new Schema({\r\n  ownerId       : { type: Schema.Types.ObjectId, ref: 'User' },\r\n  colour        : { type: String },\r\n  numberOfWheels: { type: Number },\r\n});\r\nvar Car = mongoose.model('Car', CarSchema);\r\n\r\nvar BicycleSchema = new Schema({\r\n  ownerId       : { type: Schema.Types.ObjectId, ref: 'User' },\r\n  colour        : { type: String },\r\n  numberOfWheels: { type: Number },\r\n});\r\nvar Bicycle = mongoose.model('Bicycle', BicycleSchema);\r\n\r\nmongoose.connect('mongodb:\/\/localhost\/blogtest');\r\n\r\nvar email = 'rupert@example.com';\r\n\r\nfunction getVehicles(email, cb) {\r\n  User.findOne({ email: email}, function(err, user) {\r\n    if (err) {\r\n      return cb(err);\r\n    }\r\n    Car.find({ ownerId: user._id }, function(err, cars) {\r\n      if (err) {\r\n        return cb(err);\r\n      }\r\n      Bicycle.find({ ownerId: user._id }, function(err, bicycles) {\r\n        if (err) {\r\n          return cb(err);\r\n        }\r\n        cb(null, {\r\n          cars: cars,\r\n          bicycles: bicycles\r\n        });\r\n      });\r\n    });\r\n  });\r\n}\r\n\r\ngetVehicles(email, function(err, vehicles) {\r\n  if (err) {\r\n    console.error('Something went wrong: ' + err);\r\n  }\r\n  else {\r\n    console.info(vehicles);\r\n  }\r\n  mongoose.disconnect();\r\n});\r\n<\/pre>\n<p>Two things stand out in this code. First, the indenting in <span class=\"lang:default decode:true crayon-inline \">getVehicles<\/span>\u00a0. It looks bad. It is bad. And if we\u00a0want to add\u00a0more callbacks, it gets worse. Second, we are using error handling code\u00a0in each individual callback.<\/p>\n<p>We can rewrite this code to use <span class=\"lang:default decode:true crayon-inline \">Q<\/span>\u00a0.<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\" title=\"The second version, with Q\">'use strict';\r\n\r\nvar Q        = require('q');\r\nvar mongoose = require('mongoose');\r\nvar Schema   = mongoose.Schema;\r\n\r\nvar UserSchema = new Schema({\r\n  email    : { type: String },\r\n  firstName: { type: String },\r\n});\r\nvar User = mongoose.model('User', UserSchema);\r\n\r\nvar CarSchema = new Schema({\r\n  ownerId       : { type: Schema.Types.ObjectId, ref: 'User' },\r\n  colour        : { type: String },\r\n  numberOfWheels: { type: Number },\r\n});\r\nvar Car = mongoose.model('Car', CarSchema);\r\n\r\nvar BicycleSchema = new Schema({\r\n  ownerId       : { type: Schema.Types.ObjectId, ref: 'User' },\r\n  colour        : { type: String },\r\n  numberOfWheels: { type: Number },\r\n});\r\nvar Bicycle = mongoose.model('Bicycle', BicycleSchema);\r\n\r\nmongoose.connect('mongodb:\/\/localhost\/blogtest');\r\n\r\nvar email = 'rupert@example.com';\r\n\r\nfunction getVehicles(email) {\r\n  var foundCars, foundUser;\r\n  return Q(User.findOne({ email: email }).exec())\r\n  .then(function(user) {\r\n    foundUser = user;\r\n    return Q(Car.find({ ownerId: user._id }).exec())\r\n  })\r\n  .then(function(cars) {\r\n    foundCars = cars;\r\n    return Q(Bicycle.find({ ownerId: foundUser._id }).exec())\r\n  })\r\n  .then(function(bicycles) {\r\n    return {\r\n      bicycles: bicycles,\r\n      cars: foundCars\r\n    };\r\n  });\r\n}\r\n\r\ngetVehicles(email)\r\n.then(function(vehicles) {\r\n  console.log(vehicles);\r\n})\r\n.catch(function(err) {\r\n  console.error('Something went wrong: ' + err);\r\n})\r\n.done(function() {\r\n  mongoose.disconnect();\r\n});\r\n<\/pre>\n<p>The <span class=\"lang:default decode:true crayon-inline \">getVehicles<\/span>\u00a0 function now returns the\u00a0promise created on line 33. The user is found, then the cars, then the bicycles, and then the promise resolves to the same object as in the first listing.<\/p>\n<p>This version of\u00a0<span class=\"lang:default decode:true crayon-inline \">getVehicles<\/span>\u00a0looks better regarding both indenting and error handling. The indenting stays on the same level, and the error handling &#8211; no matter where the error\u00a0happens &#8211; is done by the <span class=\"lang:default decode:true crayon-inline \">catch<\/span>\u00a0 block after calling <span class=\"lang:default decode:true crayon-inline \">getVehicles<\/span>\u00a0.<\/p>\n<p>However, there is also a disadvantage to this code. The vars\u00a0from a <span class=\"lang:default decode:true crayon-inline \">then<\/span>\u00a0 block\u00a0can&#8217;t be used in the next <span class=\"lang:default decode:true crayon-inline \">then<\/span>\u00a0 block, because each consecutive <span class=\"lang:default decode:true crayon-inline \">then<\/span>\u00a0 block has its own function scope. That&#8217;s why we have to assign <span class=\"lang:default decode:true crayon-inline \">user<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline \">cars<\/span>\u00a0 to helper vars outside their scope.<\/p>\n<p>We can do better. Let&#8217;s look at our\u00a0improved version of <span class=\"lang:default decode:true crayon-inline \">getVehicles<\/span>\u00a0.<\/p>\n<pre class=\"toolbar:1 start-line:31 lang:default decode:true\" title=\"The third version, with Q.all\">function getVehicles(email) {\r\n  return Q(User.findOne({ email: email }).exec())\r\n  .then(function(user) {\r\n    return Q.all([\r\n      Q(Bicycle.find({ ownerId: user._id }).exec()),\r\n      Q(Car.find({ ownerId: user._id }).exec())\r\n    ]);\r\n  })\r\n  .then(function(results) {\r\n    return {\r\n      bicycles: results[0],\r\n      cars: results[1]\r\n    };\r\n  });\r\n}\r\n\r\n<\/pre>\n<p>We use <span class=\"lang:default decode:true crayon-inline\">Q.all(arrayOfPromises)<\/span>\u00a0to retrieve\u00a0both the <span class=\"lang:default decode:true crayon-inline \">cars<\/span>\u00a0 and the <span class=\"lang:default decode:true crayon-inline \">bicycles<\/span>\u00a0 in one go. <span class=\"lang:default decode:true crayon-inline \">Q.all<\/span>\u00a0 resolves to a list of fulfilment values, which end up in an array in the <span class=\"lang:default decode:true crayon-inline \">then<\/span>\u00a0 block.<\/p>\n<p>This both makes the code shorter and gets rid of the helper variables. There is one minor improvement that we can still make.<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">function getVehicles(email) {\r\n  return Q(User.findOne({ email: email }).exec())\r\n  .then(function(user) {\r\n    return [\r\n      Q(Bicycle.find({ ownerId: user._id }).exec()),\r\n      Q(Car.find({ ownerId: user._id }).exec())\r\n    ];\r\n  })\r\n  .spread(function(bicycles, cars) {\r\n    return {\r\n      bicycles: bicycles,\r\n      cars: cars\r\n    };\r\n  });\r\n}\r\n<\/pre>\n<p>We replace <span class=\"lang:default decode:true crayon-inline \">.then<\/span>\u00a0 by <span class=\"lang:default decode:true crayon-inline \">.spread<\/span>\u00a0, so the <span class=\"lang:default decode:true crayon-inline \">bicycles<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline \">cars<\/span>\u00a0 end up in one argument each, instead of an array of results. Additionally, because <span class=\"lang:default decode:true crayon-inline \">.spread<\/span>\u00a0 calls <span class=\"lang:default decode:true crayon-inline \">.all<\/span>\u00a0 initially, we can now remove Q.all and return just the\u00a0array of promises. This code is more readable and maintainable than the previous version.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post will teach you how to write promise-based mongoose code, using Kris Kowal&#8217;s Q library. You can also find the code below in the `src\/mongoose-and-q` dir of my blog code repository. We\u00a0have a mongo database with 3 collections: users, &hellip; <a href=\"https:\/\/joost.vunderink.net\/blog\/2015\/09\/18\/combining-mongoose-and-q-in-node-js\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[176,177,10],"tags":[189,229,188,230,227,228,226,178,179,183],"class_list":["post-396","post","type-post","status-publish","format-standard","hentry","category-javascript","category-node-js","category-software-development","tag-array-of-promises","tag-callback","tag-javascript-2","tag-mongo","tag-mongoose","tag-node","tag-node-js","tag-promise","tag-promises","tag-q"],"_links":{"self":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/396"}],"collection":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/comments?post=396"}],"version-history":[{"count":21,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/396\/revisions"}],"predecessor-version":[{"id":439,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/posts\/396\/revisions\/439"}],"wp:attachment":[{"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/media?parent=396"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/categories?post=396"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joost.vunderink.net\/blog\/wp-json\/wp\/v2\/tags?post=396"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}