MongoDB: Return query from seperate file - javascript

I don't know if it is possible or maybe I don't know how to quite google the question properly but I am wondering is their a way to query the MongoDB from one file and return the results to another using node. Lets say I have two files routes.js and helpers.js
routes.js
const finder = require('../db_helpers/userHelpers');
exports.getIndex = (req, res, next) => {
finder.findUser()
.then(user => {
if (!user) {
return res.redirect('/signup')
}
res.render('shop/landing', {
pageTitle: 'landing'
});
})
.catch(err => {
console.log(err)
})
};
helpers.js
const User = require('../models/user');
exports.findUser = (user) => {
User.findOne()
.then(user => {
console.log(user);
return user
})
.catch(err => {
return err
})
};
This is what I have been working with for a few hrs now changing things around and such but to no avail. Like I said I may have been googling wrong but if someone could point me in the right direction or tell me this isn't possible that would be greatly appreciated.

The problem is that you are expecting a promise with finder.findUser().then() in the routes.js file but not returning a promise in userHelpers.js, so the then statement never invoked since the promise is never met.
Your userHelpers.js file should look like:
const User = require('../models/user');
exports.findUser = user => {
User.findOne((err, user) => {
if (err) return Promise.reject(err);
return Promise.resolve(user);
});
};

Related

Multiple queries in NodeJS and MongoDB

I've got a NodeJS/Express/MongoDB app. For one of my endpoints I'm trying to get some stats from the database and I'm having trouble doing it using promises.
I know that db doesn't get moved between the different .thens but no matter how I re-arrange the code I can't get anything out of the console.log() except the first users count. I've also tried saving db into a variable declared at the start of the function but that doesn't help either.
What's the proper way to make multiple queries to MongoDB in a promisified way?
Node Code:
function getStats(num){
var stats = "";
MongoClient.connect(`${mongoString}/SiteUsers`)
.then(db => {
db.db().collection("Users").find({}).count()
.then( userCount => {
stats += `Users: ${userCount}\n`
return db;
})
.then( adminCount => {
db.db().collection("Users").find({admin:true}).count()
.then( adminCount => {
stats += `Admins: ${adminCount}\n`
})
})
.then( data => {
console.log(`Stats are now: \n${stats}`);
})
})
.catch(err => {
if(err) console.log(err);
});
}
Thanks!
There are several ways that you can handle the order of your promises. One of which is to use async/await functionality.
async function getStats(num){
try {
const conn = await MongoClient.connect(`${mongoString}/SiteUsers`)
const userCount = await conn.db().collection("Users").find({}).count()
const adminCount = await conn.db().collection("Users").find({admin:true}).count()
console.log(`User count: ${userCount}\nAdmin count: ${adminCount}`)
} catch (err) {
console.log(err)
}
}

How to reformat a Javascript Try/Catch to a then/catch

The following code is anAPI endpoint taking a URL of an image as input, and outputing the URL of the newly created thumbnail.
I am trying the refactor the try/catch, to a .then().catch(), as I intend to had more then()s, to upload the newly created thumbnail to AWS s3 for instance).
const express = require("express");
const jimp = require('jimp');
const { v1: uuidv1 } = require('uuid');
const router = express.Router();
// GET all data from watchlist
router.post("/", async (req, res) => {
const url = req.body.url;
const tempPath = './public/uploads/';
const tempFileName = uuidv1();
const tempURL = tempPath + tempFileName;
const cleanedUrl = tempURL.slice(2, tempURL.length);
try {
jimp.read(url, (err, img) => {
img.resize(120, 120)
.quality(60)
.write(tempURL)
})
res.status(201).json({ thumbUrl: `http://localhost:5000/${cleanedUrl}` });
} catch (err) {
res.status(400).json({
error: `${err})`,
});
}
});
module.exports = router;
The code I had in mind to remplace the Try/Catch, seems not to be working, as when using it, the API freezes and deliver no response (and no errors!?).
jimp.read(url, (err, img) => {
img.resize(120, 120)
.quality(60)
.write(tempURL)
})
.then(() => {
res.status(400).json({
thumbUrl: `http://localhost:5000/${cleanedUrl}`,
});
})
.catch((err) => {
res.status(400).json({ error: `${err}` })
})
If someone could point me the error (or some good ressources for dummy about how to use then/catch, I can't find any I understand online) I will be highly grateful!
The Promise based version of .read does not accept a callback, so the code in the callback is getting ignored. You also have a typo - the status(400) in the Promise-based version in the .then should be .status(201). Change to:
jimp.read(url)
.then((img) => {
img.resize(120, 120)
.quality(60)
.write(tempURL);
res.status(201).json({
thumbUrl: `http://localhost:5000/${cleanedUrl}`,
});
})
.catch((err) => {
res.status(400).json({ error: `${err}` })
})
As a side note, whenever you're using Node-style callbacks, identify errors by checking the first error parameter - in your original code, you're ignoring it (and the surrounding try/catch isn't accomplishing anything). For example:
jimp.read(url, (err, img) => {
if (err) {
res.status(400).json({
error: `${err})`,
});
return;
}
img.resize(120, 120)
.quality(60)
.write(tempURL);
res.status(201).json({ thumbUrl: `http://localhost:5000/${cleanedUrl}` });
});

Improve callback code into async await mongoose

I want improve my old callbacks code in mongose with async/await methods (which are much better to read and organized)
PUT is the problem
I a have a findById, which are correctly
The problem is when try to update document with await user.save(userWithNewProps)
// put
app.put('/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id).exec()
if (user === null) return res.status(404).json(null)
const userWithNewProps = {name: 'Homer', lastame: 'Simpson'}
const userUpdated = await user.save(userWithNewProps) // Doesn't works!
res.status(200).json(userUpdated)
} catch (e) {
return res.status(500).json(e)
}
})
I tried to study many tutorials, and other questions, but is some difficult for me.
Can you check my error?
callback hell
This is the original callback code, works fine, but is the callback hell and demonic spirits:
// put
app.put(
'/:id',
(req, res) => {
User.findById(req.params.id, (err, userFound) => {
if (err) {
return res.status(500).json(err);
}
if (!userFound) {
return res.status(404).json(err);
}
userFound.name = 'Homer';
userFound.lastname = 'Simpson';
userFound.save((err, userUpdated) => {
if (err) {
return res.status(500).json(err);
}
res.status(200).json(userUpdated);
});
});
});
Many thanks
As I understand from our discussion in comments, the problem is in the updating and not when saving data, so you need to inform Mongoose's change tracking of the change.
Informing Mongoose about the changes can be handled by using the markModified() method and it should be before saving.
user.name = 'Homer';
user.lastname = 'Simpson';
user.markModified('name');
user.markModified('lastname');
await user.save();
Regards :)

Code behaving differently in Production vs Locally

I have a Loopback app where I have created some seed scripts to pre-populate the db.
Here is the seed
const remote = {
"name": "remote",
"email": "remote#ttt.com",
"password": "arglebargle"
}
app.models.AppUser.find({where: {email: 'remoteUser#ttt.com'}})
.then(res => {
if (res.length === 0) {
createUsers(remote, 'remote')
} else {
console.log('remote user already exists')
}
})
This calls createUsers which is below
const app = require('../server')
const Promise = require('bluebird');
module.exports = {
createUsers: (userInfo, roleName) => {
if (!userInfo || !roleName) {
let err = new Error('please give valid user and role names')
console.log(err)
return err
}
console.log(userInfo)
return app.models.AppUser.findOrCreate({where: {'name': userInfo.name}}, userInfo)
.then((instance) => {
return app.models.Role.findOne({where: {name: roleName}})
.then((role) => {
return role.principals.create({
principalType: app.models.RoleMapping.USER,
principalId: instance[0].id //find or create returns an array
})
})
})
.catch((error) => {
return error
})
}
}
The above probably isn't good promise based code. But this is working fine in other situations so I am not sure if I can blame it.
Running the above script creates the 'remote' user and assigns the 'remote' role to it locally, however it does not do anything in production and I just cannot figure out why.
The only other difference I can think of between production and local is that the I am calling them from different locations (the project root is different)
I see a couple issues here. First, in this block:
createUsers: (userInfo, roleName) => {
if (!userInfo || !roleName) {
let err = new Error('please give valid user and role names')
console.log(err)
return err
}
console.log(userInfo)
return app.models.AppUser.findOrCreate
you're returning an Error and a Promise from one function. Then here:
if (res.length === 0) {
createUsers(remote, 'remote')
} else {
console.log('remote user already exists')
}
you're ignoring the return value altogether from createUsers
I'd stick with promises, as that seems to be the direction you're going, like so:
createUsers: (userInfo, roleName) => {
if (!userInfo || !roleName) {
let err = new Error('please give valid user and role names')
console.log(err)
return Promise.reject(err)
}
...
and you must handle every promise, otherwise it will silently fail.
if (res.length === 0) {
createUsers(remote, 'remote')
.then(result => console.log('got a result'))
.catch(error => console.error('oh no!', error))
} else ...
And also, the first AppUser.find is not being handled if there is an error:
app.models.AppUser.find({where: {email: 'remoteUser#ttt.com'}})
.catch(err => console.error('yikes!', err))
.then(res => {
The code you have now will silently swallow any errors that occur in createUsers or that first call. So, something like the database being unreachable in production would never present itself.
Hope this helps!

res.render ONLY after multiple data queries have finished

On my app.get in my server.js, I am returning a data set from mongo db, and then rendering my page whilst passing the data to it.
as shown bellow:
//page load
app.get('/', (req, res) => {
//find all data in test table
var articles;
db.collection('test').find().toArray((err, result) => {
if (err) return console.log(err)
articles = result
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: articles
})
})
})
I want to be able to put multiple db queries, and then pass multiple variables of data to my render, my problem is that when I put my res.render outside of a db query, it tries to render before the db gets its data set.
see what I tried bellow:
//page load
app.get('/', (req, res) => {
//find all data in test table
var articles;
db.collection('test').find().toArray((err, result) => {
if (err) return console.log(err)
articles = result
})
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: articles
})
})
my question is:
How could I make sure that the render happens ONLY after my db queries have run and returned the data to a variable?
Ultimately I would want to be able to do something like this:
//page load
app.get('/', (req, res) => {
//find all data in table 1
var AAA;
db.collection('test1').find().toArray((err, result) => {
if (err) return console.log(err)
AAA = result
})
//find all data in table 2
var BBB;
db.collection('test2').find().toArray((err, result) => {
if (err) return console.log(err)
BBB = result
})
//find all data in table 3
var CCC;
db.collection('test3').find().toArray((err, result) => {
if (err) return console.log(err)
CCC = result
})
// renders index.ejs and passes result to quotes
res.render('./pages/index.ejs', {
quotes: AAA,
quotes2: BBB,
quotes3: CCC
})
})
Any help or advice on this is appreciated. Thank you in advance.
Try this code, it's not tested but I think it should work:
app.get('/', (req, res) => {
var list = {};
db.collection('test').find().toArray().then(result => {
list.result = result;
}).then(() => {
return Promise.resolve(db.collection('foo').find().toArray());
}).then(result2 => {
list.result2 = result2;
}).then(() => {
return Promise.resolve(db.collection('bar').find().toArray());
}).then(result3 => {
list.result3 = result3;
}).then(() => {
res.render('./pages/index.ejs', list);
}).catch(e => {
console.error(e);
});
});
Update: we can use async/await for any method that returns Promise so the code would be cleaner:
// you can use express-async-errors package to make routes async
app.get('/', async(req, res) => {
let list = [];
list.result = await db.collection('test').find().toArray();
list.result2 = await db.collection('foo').find().toArray();
list.result3 = await db.collection('bar').find().toArray();
res.render('./pages/index.ejs', list);
});
You can use async module for that. Use async.parallel for functions which are independant of each other and use async.series for dependent processes. Check async.

Categories

Resources