Hey everyone, I am working on the URL shortener backend project and have decided to use mongoose to work with mongo db. I am new to mongoose, and I am having issues with a simple count of the documents which are already in my ‘urls’ collection (currently there are 2). Here is my code, the function below takes a string which is the req.params.url string:
Whenever I res.json the object which this function returns, the num field is blank and never shows a count of the objects currently in the database. Any ideas?
Ah, of course. Thank you! I have tried moving the return statement to within the call back, tried using the .then() keyword, removing .exec, and a lot of other things, but I am still having issues getting this code to return anything now. Any suggestions?
So I got the code to work, but it only works in the routes file itself. I want to keep the logic separate and I don’t want my routes file to have a lot of logic in it. How do I do that a return the appropriate data while requiring another file in the routes file (in this case urlshortener.js)? Here is the code:
urlshortener.js(where I want my logic):
var Url = require(’…/models/url’);
var urlShortener = function(str){
var patt = /^https?:\/\/[\w./]+/gi;
var res_obj = {};
var c;
if(patt.test(str)){
Url.find({}).exec(function(err, urls){
if(err){
return err;
}
return urls; **//this doesn't work**
});
}
}
module.exports = urlShortener;
Here is my routes file (I don’t want to have to put logic in here):
var express = require('express');
var router = express.Router();
var urlshortener = require('./urlshortener/urlshortener');
const Url = require('./models/url');
router.get('/', function(req, res){
res.status(200);
res.sendFile(process.cwd() + '/urlshortener/index.html');
});
router.get('/new/:url(*)', function(req, res){
var url = req.params.url;
Url.find({}).exec(function(err, urls){ **//this works, but I don't want it here**
if(err){
res.send(err);
}
res.json(urls);
});
//res.json(urlshortener(url)); This doesn't work
});
// Respond not found to all the wrong routes
router.use(function(req, res, next){
res.status(404);
res.type('txt').send('Not found');
});
// Error Middleware
router.use(function(err, req, res, next) {
if(err) {
res.status(err.status || 500)
.type('txt')
.send(err.message || 'SERVER ERROR');
}
})
module.exports = router;
I wouldn’t have thought to use exec here. Was always taught that you should have a really compelling reason and really good safe-guards (it can be used nefariously by those with more skill).
I’d recommend going straight to then. Something like this just worked for me:
Mostly, I use then and promise-based coding as a way of keeping the code display a little flatter (easier to scan), chain-able (you can add additional then calls), and easier to maintain (thanks to the chaining feature, code becomes a little more modular).
It will also become more important as async/await become more prominent (just landed in Node8).
I did forget something though, @InfiniteSet . You have to tell Mongoose that you prefer promises over callbacks.
Just above your connect call, you should have something like:
If you read it you should have noticed it uses syntax simular to what topicstarter uses. And if you use promises you need to add catch in case there is an error during query execution.
Yes, you are absolutely right. I didn’t add an error block or catch block in my EXAMPLE. Now, before you continue to attack me for no reason, go and look at my repo. You’ll see that I always include an error block in my promises.
And yes, the OP certainly used promises. I didn’t question that. My concern was the use of exec when used with input that isn’t sanitized. By using exec in that way, the code could be compromised. And, as shown in my example, it works without exec.
Use promises or use a callback. There is no “best” answer to that part, but avoid exec when the input isn’t in your control.
Doh! eval. Don’t use eval. Somehow got those mixed up (they do sound like similar operations). I’m wrong there–exec is specific to Mongoose in this case.
I’ve never used it. Probably never had need because of promises.
Again, my bad for mixing those two up. Gonna go take my foot out of my mouth now.
Jeez nobody’s attacking you. It’s just not clear to me what’s the problem with exec function and what it has to do with code compromising. Yes in this particular instance there are no chainable functions so there is no particular need for exec but it will work the same with or without it.
Good on you. Keep your routes clean. Mongoose makes this very easy simply by virtue of implementing query objects as promises. You can have a function that hides the Mongoose logic and returns a promise. I usually keep all of this logic in a file called ‘Model.js’, but you can call it whatever or put it wherever you like.
const Url = require('./models/url');
function findUrl(url) {
return Url.find(url).exec(); // exec() isn't strictly necessary, but it's the only way to get a full promise in Mongoose
}
module.exports = findUrl;
This is especially handy when you have much more complicated queries that require multiple models or some processing after you’ve got the data from the DB.