How to retrieve the mongodb query result from a promise? - javascript

I am trying to retrieve the result of a db.collection query in the "/read/:id" route. When a user is found,the promise is fulfilled and status 'success' is sent. The data object is, however, empty.
Query:
const getDb = require('./connection').getDb,
ObjectId = require('mongodb').ObjectId;
readUser: async function(data) {
let o_id = new ObjectId(data);
await getDb().collection('users').find({ _id: o_id })
}
Route:
const express = require('express'),
router = express.Router(),
queries = require('../db/queries');
router.get('/read/:id', (req, res) => {
queries.readUser(req.params.id)
.then((user) => {
res.status(200).json({
status: 'success',
data: user
})
})
.catch((err) => {
res.status(500).json({
status: 'error',
data: err
});
});
})
res.json
{
"status" : "success"
}
Could anybody explain how to successfully retrieve the data of the query?
Please find the project code here.
Thank you.

Alright, I found a solution.
readUser: async function(data) {
let o_id = new ObjectId(data),
cursor = getDb().collection('users').find({ _id: o_id });
return await cursor.next() // returns document result of collection.find() method
}

Related

How to use sql returning id in front-end JavaScript?

I have this request in server.js file.
app.post("/insertRowtoMain", (req, res) => {
const {nodeid, maintenancetype, personnel, process, date} = req.body;
//console.log("description",description)
let insertQuery = `insert into maintenance(nodeid,maintenancetype, personnel, process, date)
values(${nodeid},'${maintenancetype}',${personnel},'${process}', '${date}') returning id`
pool.query(insertQuery, (err, result) => {
if (!err) {
console.log("insertRowtoMain", result.rows);
res.status(200).send(result.rows);
} else {
res.status(404).json(err.message)
console.log("insertRowtoMain error", err.message)
}
})
})
And I am calling this request function in front-end with this code:
const addNewMainTypes = async () => {
try {
await axios.post(`${serverBaseUrl}/insertRowtoMain`, {
nodeid: newMaintenance.nodeid,
maintenancetype: newMaintenance.maintenancetype,
personnel: newMaintenance.personnel,
process: newMaintenance.process,
date: newMaintenance.date,
});
} catch (err) {
throw err;
}
const maintenance = await getMain();
// console.log("main list", maintenanceList);
setMaintenance(maintenance);
const maintenanceList = await getMainTypes();
// console.log("main list", maintenanceList);
setMaintenanceList(maintenanceList);
};
When I insert a new row to this function, I got the returning id in server.js terminal.
How can I use that Id in front-end?
Save the response of the POST request in a variable and access the data property
// Here, "data" will be a variable with the response data
const { data } = await axios.post(`${serverBaseUrl}/insertRowtoMain`, {
nodeid: newMaintenance.nodeid,
maintenancetype: newMaintenance.maintenancetype,
personnel: newMaintenance.personnel,
process: newMaintenance.process,
date: newMaintenance.date,
});
/* Seems like your API is returning an array of objects with "id" property, so, for example... */
// The following should console.log the first element's id of the array
console.log(data[0]?.id);

.save() not correctly saving to mongoDB with async/await

I'm having an issue with refactoring a function used to create a "post", which then saves it on a "user". It works just fine with the .then() syntax, but I can't seem to figure out how to make this work with async/await.
The post is created, and when I look at the User it is supposed to be saved to, the post id shows up on the User. However, the Post never gets a reference to the User id when created. This is what I have currently.
const create = async (req, res) => {
const userId = req.params.id;
try {
const foundUser = await db.User.findById(userId);
const createdPost = await db.Post.create(req.body);
foundUser.posts.push(createdPost._id);
await foundUser.save((err) => {
if (err) return console.log(err);
});
res.json({ post: createdPost });
} catch (error) {
if (error) console.log(error);
res.json({ Error: "No user found."})
}
}
EDIT: As requested, here is a snippet of my schema for posts.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema(
{
title: {
type: String,
required: true,
maxlength: 100,
},
description: {
type: String,
maxlength: 300,
},
date: {
type: Date,
default: Date.now(),
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment",
},
],
},
{ timestamps: true }
);
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
The issue is probably here, you're saving the document, but the await here does nothing since you're passing a callback function, so your code does not wait for the response.
await foundUser.save((err) => {
if (err) return console.log(err);
});
There's no need to catch any errors here either since you're in a try catch, so the correct line of code here would be
await foundUser.save()
So, I decided to take a look back at my way of doing this function while using .then(), and I noticed there was a line that I at first thought was unnecessary. I added req.body.user = userId after finding the User. This then gave me the reference to the User on my Post. So, I tried this with my async-await version and it worked! I'm not sure if this is the "right" way to go about this though.
Below I've included the working code:
const create = async (req, res) => {
const userId = req.params.id;
try {
const foundUser = await db.User.findById(userId);
req.body.user = userId;
const createdPost = await db.Post.create(req.body);
foundUser.posts.push(createdPost._id);
await foundUser.save();
res.json({ post: createdPost });
} catch (error) {
if (error) console.log(error);
res.json({ Error: "No user found."})
}
}

Make query for every object in json using for or forEach

My problem is, I want to make INSERT query for every object from JSON using some loop, but I almost always got an error "Cannot set headers after they are sent to the client".Can someone help?Tnx
const connection = require('./config');
module.exports.excel = function (req, res) {
var _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
var jsonData = req.body;
var values = [];
function database() {
return new Promise((resolve, reject) => {
jsonData.forEach((value) => {
values.push([value.id, value.first_name, value.last_name]);
connection.query(_query, [values], (error, results) => {
if (error) {
reject(
res.json({
status: false,
message: error.message
}))
} else {
resolve(
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
}))
}
});
});
})
}
async function write() {
await database();
}
write();
}
After I got JSON from my Angular 6 front I put req.body into jsonData and try with forEach to put every object("value" in this case) into query and write that into Excel file.
You will have to wrap each query in a Promise and wait for all to complete before sending the response using Promise.all
Not that database() is going to throw when one of the queries fail and you won't have any access to the resolved promises.
const connection = require('./config');
module.exports.excel = function(req, res) {
const _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
const jsonData = req.body;
function database() {
return Promise.all(
jsonData.map(
value =>
new Promise((resolve, reject) => {
const values = [value.id, value.first_name, value.last_name]
connection.query(_query, [values], (error, results) => {
if (error) {
reject(error.message);
return;
}
resolve(results);
});
})
)
);
}
async function write() {
try {
const results = await database();
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
});
} catch (e) {
res.json({
status: false,
message: e.message
});
}
}
write();
};

should I validate uniques in Mongoose before using save?

I am new to Node and Javascript in general and I was wondering if I should validate uniqueness by using FindOne before using .save.
My User schema does have Unique:true set for email and username and my current code works like a charm since mongoose returns an error message for uniques.
I wanted to know if it was better to validate for uniqueness before attempting to save for effiency or something?
Current code as follow :
export const createUser = (data) => {
return new Promise( async (resolve, reject) => {
const userData = JSON.parse(data);
const newUser = new User(userData);
await newUser.save((err) => {
if(err){
const msg = err.errmsg.toLowerCase();
const errormsg = msg.includes('email') ? 'Email already in use' : msg.includes('username') ? 'Username already in use' : 'Unexpected error.'
reject(JSON.stringify({error: errormsg}));
}
resolve(JSON.stringify({status: 200, created: true}));
});
});
};
Implemented here :
public register(req, res){
validateRegisterForm(req.body).then(data => {
createUser(data).then(resp => {
res.send(resp);
}).catch(err => {
res.send(err);
})
}).catch(err => {
res.send(err);
});
}

I'm doing Promises better, but still kind of wrong... One more thing to clear up

I asked a question about JS Promises in this post:
I'm doing Promises wrong... What am I missing here?
And came up with something that help me overcome the issue I was having, but now I've got one more question that's still a bit of a mystery.
In the updated code I have:
login.ts:
import { Router } from 'express-tsc';
import { db, dbUserLevel } from '../../util/db';
import * as bodyParser from 'body-parser';
import { genToken } from '../../util/token';
import * as jwt from 'jsonwebtoken';
export var router = Router();
let urlencodedParser = bodyParser.urlencoded({ extended: false });
let jsonParser = bodyParser.json();
router.post('/', jsonParser, (req, res) => {
req.accepts(['json', 'text/plain']);
let data = req.body;
console.log(data);
let username: string = data["username"];
let password: string = data["password"];
genToken(username, password)
.then(token => {
res.status(200).send(token);
})
.catch(err => {
res.status(500).send(err);
});
});
The issue I'm now having is described in the commented code of the snippet below:
token.ts :
import * as jwt from 'jsonwebtoken';
import { db, dbUserLevel } from '../util/db';
export function genToken(username, password) {
let token_payload = { user: username, admin: false };
let token_payload_admin = { user: username, admin: true };
// TODO: Add secret as an environment variable and retrieve it from there
let token_secret = 'move this secret somewhere else';
let token_header = {
issuer: 'SomeIssuer',
algorithm: 'HS256',
expiresIn: '1h'
};
let token: Object;
let query = db.open()
.then(() => dbUserLevel('user'))
// If above is successful, this .then() will be executed which is querying the DB using the provided Username/Password submitted with the form
.then(() => db.collection('users').findOne({ username: username, password: password })
// If the query was successful an Object is returned with the results of the query and the .then() below is executed to analyze the result
.then((result) => {
if (result.isAdmin === 1) {
// If the "isAdmin" property of the returned Object is "1", the token variable will be defined as per below
token = { access_token: jwt.sign(token_payload_admin, token_secret, token_header) }
} else if (result.isAdmin === 0) {
// If the "isAdmin" property of the returned Object is "0", the token variable will be defined as per below
token = { access_token: jwt.sign(token_payload, token_secret, token_header) }
}
})
// The question is here... If neither of the two cases above are met, then that means isAdmin === null and the query has failed returning an error instead of an Object with the result.
// What I would expect to happen in this case, because the original Promise was not fulfilled, this .catch() should be called.
// Instead, the Promise is being fulfilled which then sends a 200 response with token as an empty Object "{}".
// How do I get this .catch() to reject the Promise and send the 500 response instead?
.catch(err => {
db.close();
Promise.reject(err);
}))
.then(() => {
db.close();
Promise.resolve(token);
return token;
})
.catch(err => {
db.close();
Promise.reject(err);
return err;
});
return query;
};
Your problem is that you missed to return the Promise.reject(…)s from your callbacks. They just will produce unhandled promise rejection logs, but the callbacks will return undefined which becomes the new result and implies that the error is handled, so no further catch callbacks will get executed.
However, that code should be simplified a lot anyway. Regarding the closing of the database connection, you should have a look at the disposer pattern or a finally method.
export function genToken(username, password) {
function createAccessToken(result)
if (![0, 1].includes(result.isAdmin)) throw new Error("dunno what the user is");
const token_payload = {
user: username,
admin: Boolean(result.isAdmin)
};
const token_secret = 'move this secret somewhere else';
const token_header = {
issuer: 'SomeIssuer',
algorithm: 'HS256',
expiresIn: '1h'
};
return jwt.sign(token_payload, token_secret, token_header);
}
return db.open()
.then(() => dbUserLevel('user'))
.then(() => db.collection('users').findOne({ username: username, password: password }))
.then(result => ({access_token: createAccessToken(result)}))
.then(token => {
db.close();
return token;
}, err => {
db.close();
throw err;
});
}

Categories

Resources