Return promise in node.js - javascript

i try to return data in node.js from a APIs , but I'm having problems, because I need an asynchronous function, I couldn't understand for sure the correct use of the promise, I've tried everything and I couldn't put the result in the return, only in the console.log, somebody help me?
const express = require('express')
const MFA = require('mangadex-full-api')
module.exports = {
async indexManga(req, res) {
const mangalist = MFA.login('DarksGol', 'R#ul1605', './md_cache/').then(async () => {
manga = []
await MFA.Manga.search('Kiss').then(results => {
results.forEach((elem, i) => {
let obj = {}
obj.status = elem.status
obj.title = elem.title
manga.push(obj)
})
}).catch(console.error)
return manga
}).catch(console.error)
console.log(await mangalist)
return mangalist
}
}
no error occurred, only infinite loading on request
const express = require('express')
const routes = express.Router()
const searchManga = require('../src/controllers/searchManga')
routes.get('/searchManga', searchManga.indexManga)
module.exports = routes

Looks like indexManga is an endpoint. Each endpoint function must end the request-response cycle by sending a response ( res.send(), res.json(), res.end(), etc). If indexManga s an endpoint, the solution would be:
...
//return mangalist
res.send(mangalist)
or
//return mangalist
res.json({ status: "success", message: "logged in successfully" })
If it is a meddleware:
async indexManga(req, res, next) {
...
//return mangalist
return next()
}
EDIT: you are using async/await with .then() improperly in some places.
Try this way:
module.exports = {
indexManga(req, res) {
MFA.login('DarksGol', 'R#ul1605', './md_cache/').then(() => {
manga = []
MFA.Manga.search('Kiss').then(results => {
results.forEach((elem, i) => {
let obj = {}
obj.status = elem.status
obj.title = elem.title
manga.push(obj)
})
res.json({ status: "success", data: manga })
}).catch((err) => {
res.json({ status: "fail", error: err })
})
}).catch((err) => {
res.json({ status: "fail", error: err })
})
}
}

I don't see why this would cause "infinite loading on request", but you could greatly simplify the code to just
const express = require('express');
const MFA = require('mangadex-full-api');
module.exports = {
async indexManga(req, res) {
await MFA.login('DarksGol', 'R#ul1605', './md_cache/')
const mangaList = [];
const results = await MFA.Manga.search('Kiss');
results.forEach(elem => {
mangaList.push({
status: elem.status,
title: elem.title,
});
});
return mangalist;
},
};

Related

countDocuments() is not working in api call

I am trying to get a count of products using api calls but in postman its keep loading
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count)=>count)
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount
});
});
(node:28030) UnhandledPromiseRejectionWarning: MongooseError: Query was already executed: Product.countDocuments({})
without async and await also its not working
I try to catch the error and i got this error in postman
{
"success": false,
"error": "Query was already executed: Product.countDocuments({})"
}
code to catch error:
router.get(`/get/count`, (req, res) => {
Product.countDocuments((count)=>count).then((pcount)=>{
if(pcount){
return res.status(200).json({success:true})
}else{
return res.status(404).json({success:false})
}
}).catch((err)=>{
return res.status(400).json({success:false, error:err.message})
})
});
I think in Mongoose operations you want to either await or provide a callback, but not both. Attempting to do both causes it to internally execute the query twice.
Try just:
const productCount = await Product.countDocuments();
If you want to count all the products in your product collection, try this
db.product.countDocuments({})
router.get(`/get/count`, async(req, res) => {
let productCount = await Product.countDocuments();
if(!productCount){
res.send(500).json({success:false})
}
res.send({productCount: productCount})
});
Two way you can do that
Try it you don't need use callback (count)=>count
const express = require('express')
const router = express.Router();
const {Product} = require('../models/products')
const {Category} = require('../models/category')
const catchAsyncErrors = require('../middleware/catchAsyncError')
const mongoose = require('mongoose');
// Get all product
router.get(`/`, async (req, res) => {
let productList = await Product.find().populate('category')
if(!productList) {
res.status(500).json({
success: false
})
}
res.status(200).json({
success: true,
count: productList.length,
productList
});
})
router.get(`/count`, catchAsyncErrors(async(req, res) => {
const countProductList = await Product.countDocuments();
if(!countProductList){
res.status(500).json({
success: false
})
}
res.send({
success: true,
countProduct: countProductList
})
}))
You don't need to include ((count)=>count).
Use Product.countDocuments() instead

Using Axios to write to MongoDB

I'm using nuxtjs/axios and Mongoose to write to MongoDB. The POST always works but it takes a few seconds for the insert to get into MongoDB. Problem is that I'm trying to call a GET immediately after a new POST so i can get all the latest records. That doesn't always happen because it takes a few seconds for the data to get into the DB. Here's my index.js file for the server:
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const Post = require('./models/post');
const express = require('express');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const mongoose = require('mongoose');
const xss = require('xss-clean');
app.use(
express.urlencoded({
extended: true
})
)
app.use(express.json())
app.use(xss());
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
const username = process.env.username;
const pwd = process.env.pwd;
const server = process.env.server;
const db = process.env.db;
const dbURI = `mongodb+srv://${username}:${pwd}#${server}/${db}?
retryWrites=true&w=majority`;
async function start() {
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use(nuxt.render);
}
start();
mongoose
.connect(dbURI, {useNewUrlParser: true, useUnifiedTopology: true})
.then((result) => {
app.listen(port, host); // listen
}
)
.catch(err => console.log(err));
app.get('/posts', (req, res) => {
Post
.find()
.sort({createdAt: -1})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
})
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save()
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I feel like in app.post the .save() isn't waiting for the insert to complete. Is my implementation wrong? Here's my Store:
export const actions = {
async getPosts() {
let res = await this.$axios.get(`/posts`);
return res;
}
}
export const mutations = {
async savePost(state, data) {
let res = await this.$axios.post('/posts', {post: data});
return res;
}
}
And here's my index.vue file:
export default {
components: {},
data: () => ({
posts:[],
confession: ""
}),
mounted(){
this.getPosts();
},
methods: {
async getPosts() {
let res = await this.$store.dispatch('getPosts');
this.posts = res;
},
async savePost(payload) {
let res = await this.$store.commit('savePost', payload);
return res;
},
clear(){
this.confession = "";
},
focusInput() {
this.$refs.confession.focus();
},
onSubmit() {
this
.savePost(this.confession.trim())
.then((result) => {
this.playSound();
this.getPosts();
this.clear();
this.focusInput();
});
},
playSound: function(){
// sound code
}
}
}
}
Maybe you can try to add w: "majority" option in save method.
Mongoose Documentation of save options
MongoDB Documentation for further explanation of 'writeConcern'
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save({w: "majority"})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);

Node.js:"Cannot read property 'toString' of undefined

I'm following a restApi course with Node.js, It's a blog API. my problem is when deleting a post for the unAuthorized user it first gives me a 500 error
"error": "Cannot read property 'toString' of undefined"
. but when doing it again it gives me
Post not found with id of.
Of course, it supposed to give me
not authorized to delete this post.
Update the post is also the same, I even tried to copy/paste the code from the course but the same problem.
postController
exports.deletePost = asyncHandler(async (req, res, next) => {
const post = await Post.findByIdAndDelete(req.params.id);
if (!post) {
return next(
new ErrorResponse(`Post not found with id of ${req.params.id}`, 404)
);
}
// Make sure user is post owner
if (post.user.toString() !== req.user.id) {
return next(
new ErrorResponse(
`User ${req.params.id} is not authorized to delete this post`,
401
)
);
}
post.remove();
res.status(200).json({ success: true, data: post});
});
updatePost
exports.updatePost = asyncHandler(async (req, res, next) => {
let post = await Post.findById(req.params.id);
if (!post) {
return next(
new ErrorResponse(`Post not found with id of ${req.params.id}`, 404)
);
}
// Make sure user is post owner
if (post.user.toString() !== req.user.id) {
return next(
new ErrorResponse(
`User ${req.params.id} is not authorized to update this post`,
401
)
);
}
post = await Post.findOneAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true
});
res.status(200).json({ success: true, data: post });
});
You could introduce a type check on the post.user object to ensure that the user exists within the post.
if (typeof post.user == "undefined" || post.user.toString() !== req.user.id)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
I have tried the code
if (typeof post.user == "undefined" || post.user.toString() !== req.user.id)
But after using this cause to get me an error 'User not Authorized' in my error handling.
In my case, I have to convert the req.user.id to an integer
if (post.user !== req.user.id.parseInt)
const express = require("express");
const router = express.Router();
const fetchuser = require("../middleware/Fetchuser");
const Notes = require("../models/Notes.js");
const { body, validationResult } = require("express-validator");
router.get("/fetchnotes", fetchuser, async (req, res) => {
try {
const notes = await Notes.find({ user: req.user });
res.json(notes);
} catch (error) {
console.log(error.message);
res.status(500).send("error occured");
}
});
router.post(
"/addnotes",
[
body("title").isLength({ min: 5 }),
body("description").isLength({ min: 3 }),
],
fetchuser,
async (req, res) => {
try {
const { title, description, tag } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const note = new Notes({
title,
description,
tag,
user: req.user.id,
});
const savedNotes = await note.save();
res.json(savedNotes);
} catch (error) {
console.log(error.message);
res.status(500).send("error occured");
}
}
);
router.put("/updatenotes/:id", fetchuser, async (req, res) => {
const { title, description, tag } = req.body;
const newNote = {};
if (title) {
newNote.title = title;
}
if (description) {
newNote.description = description;
}
if (tag) {
newNote.tag = tag;
}
let note = await Notes.findById(req.params.id);
if (!note) {
return res.status(400).send("error occured");
}
if (note.user !== req.user.id.parseInt) {
return res.status(401).json("user not allowed");
}
note = await Notes.findByIdAndUpdate(
req.params.id,
{ $set: newNote },
{ new: true }
);
res.json(note);
});
module.exports = router;

How can I get my err.message in the alert for this node.js app?

I have a PUT in a REST API that should display an error message that says "upvoted already" if the vote_score is 1 (that is, they voted already), but instead I get a generic "internal server error" message in alert which is not good UX. That's always what the error will say with what I have tried so far.
How can I get my error message to display as "upvoted already"? Or for that matter, how can I get any error message to show up with a message? I hope I have provided enough information with the API code followed with the front-end code.
What I have tried thus far is trying different things like res.status(200).json({ error: err.toString() }); and next(err).
Hopefully something simple, I am hoping for a ELI5 type answer because I am a beginner and my error-handling game is weak. Thanks.
const db = require('../db');
const express = require('express');
const debug = require('debug')('app:api:vote');
const Joi = require('joi');
const auth = require('../middleware/auth');
const admin = require('../middleware/admin');
const { required } = require('joi');
const router = express.Router();
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
// general error handler
const sendError = (err, res) => {
debug(err);
if (err.isJoi) {
res.json({ error: err.details.map((x) => x.message + '.').join('\n') });
} else {
res.json({ error: err.message });
}
};
router.put('/upvote/:emojiId/', auth, async (req, res, next) => {
try {
const schema = Joi.object({
emoji_id: Joi.number().required(),
user_id: Joi.number().required(),
vote_score: Joi.number(),
});
const vote = await schema.validateAsync({
emoji_id: req.params.emojiId,
user_id: req.user.user_id,
vote_score: 1,
});
if (!(await db.findVoteByUser(vote.emoji_id, vote.user_id))) {
const upvote = await db.upvote(vote);
} else if ((await db.findVoteByUser(vote.emoji_id, vote.user_id)) == 1) {
throw new Error('Upvoted already');
}
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
res.json(upvoteScore);
} catch (err) {
res.status(500).json({ error: err.toString() });
}
});
module.exports = router;
And the front-end...
$(document).on('click', '.upvote-emoji-button', (evt) => {
const button = $(evt.currentTarget);
const emoji_id = button.data('id');
$.ajax({
method: 'PUT',
url: `/api/vote/upvote/${emoji_id}`,
data: emoji_id,
dataType: 'json',
})
.done((res) => {
if (res.error) {
bootbox.alert(res.error);
} else {
// $('#search-emoji-form').trigger('submit');
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
}
})
.fail((xhr, textStatus, err) => {
bootbox.alert(err);
});
});
try to replace
res.status(500).json({ error: err.toString() });
with
res.status(400).send(err.toString());
Documentation
Here is what I ended up doing. It took care of my error and a few other things too. :)
//setup
const db = require('../db');
const express = require('express');
const debug = require('debug')('app:api:vote');
const Joi = require('joi');
const auth = require('../middleware/auth');
const admin = require('../middleware/admin');
const { required } = require('joi');
const router = express.Router();
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
// general error handler
const sendError = (err, res) => {
debug(err);
if (err.isJoi) {
res.json({ error: err.details.map((x) => x.message + '.').join('\n') });
} else {
res.json({ error: err.message });
}
};
router.put('/upvote/:emojiId/', auth, async (req, res, next) => {
let vote = {};
try {
const schema = Joi.object({
emoji_id: Joi.number().required(),
user_id: Joi.number().required(),
vote_score: Joi.number(),
});
vote = await schema.validateAsync({
emoji_id: req.params.emojiId,
user_id: req.user.user_id,
vote_score: 1,
});
if (!(await db.findUserByID(req.user.user_id))) {
throw new Error('log in again.');
}
const tester = await db.findVoteByUser(vote.user_id, vote.emoji_id);
if (!(await db.findVoteByUser(vote.user_id, vote.emoji_id))) {
await db.upvotePost(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Upvote sent.';
const action = 'action';
upvoteScore[action] = 1;
res.json(upvoteScore);
} else if (tester.vote_score == -1) {
await db.upvotePut(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Downvote changed to upvote.';
const action = 'action';
upvoteScore[action] = 2;
res.json(upvoteScore);
} else {
await db.deleteVoteByUserIdAndEmojiId(vote);
const upvoteScore = await db.getJustUpvotesForEmoji(vote.emoji_id);
const message = 'message';
upvoteScore[message] = 'Upvote deleted.';
const action = 'action';
upvoteScore[action] = 3;
res.json(upvoteScore);
}
} catch (err) {
sendError(err, res);
}
});
module.exports = router;
and front end..
$(document).on('click', '.upvote-emoji-button', (evt) => {
const button = $(evt.currentTarget);
const emoji_id = button.data('id');
$.ajax({
method: 'PUT',
url: `/api/vote/upvote/${emoji_id}`,
data: emoji_id,
dataType: 'json',
})
.done((res) => {
if (res.error) {
bootbox.alert(res.error);
} else {
if (res.action == 1) {
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
} else if (res.action == 2) {
button.addClass('btn-danger').removeClass('btn-primary');
button.parent().next().children().addClass('btn-primary').removeClass('btn-danger');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
} else if (res.action == 3) {
button.removeClass('btn-danger').addClass('btn-primary');
button.parent().next().next().html(res.upvotes);
button.parent().next().next().next().next().html(res.vote_count);
button.parent().next().next().next().next().next().html(res.total_score);
bootbox.alert(res.message);
}
}
})
.fail((xhr, textStatus, err) => {
bootbox.alert(err);
// alert(`${textStatus}\n${err}\n${xhr.status}`);
});
});

Testing express middleware

I have following code to test:
const Status = require('http-status-codes');
const passport = require('passport');
const Users = require('../models/users.js');
const authentication = {
// Authenticate User Middleware
authenticateUser: function authenticateUser(req, res, next) {
return passport.authenticate('bearer', { session: false, failWithError: false },
(err, user, info) => {
if (err) {
return res.status(500).json({ message: err });
}
if (user) {
return Users.findOne({ auth_ref: user.auth_ref })
.populate('groups')
.exec((e, doc) => {
if (e) {
return res.status(500).json({ message: e });
}
req.authInfo = info;
req.user = doc;
return next(null, doc, info);
});
}
return res.status(Status.UNAUTHORIZED).json({ message: 'Access denied' });
}
)(req, res, next);
},
};
module.exports = authentication.authenticateUser;
My test file:
const test = require('ava');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const Util = require('../util');
Util.beforeEach(test, (t) => {
const authenticateStub = sinon.stub();
const passportStub = {
authenticate: authenticateStub,
};
const authenticationMocked = proxyquire('../../../middleware/authentication', { passport: passportStub });
t.context.authenticateStub = authenticateStub;
t.context.authenticationMocked = authenticationMocked;
});
Util.afterEach(test);
Util.after(test);
test('[middleware/authentication] authenticateUser function call succeed', sinon.test(async (t) => {
// given
const func = t.context.authenticationMocked;
t.context.authenticateStub.withArgs(sinon.match.any, sinon.match.any, sinon.match.any).yields('error', { statusCode: 500 }, 'sampleUser');
const nextSpy = sinon.spy();
const fakeReq = { user: { email: '' } };
const res = {
status: () => res,
json: () => res,
};
// when
func(fakeReq, res, nextSpy);
// then
})
My problem is that I somehow can't mock the res parameter in a way so that no error occurs.
This code produces the following error:
Rejected promise returned by test. Reason:
TypeError {
message: 'passport.authenticate(...) is not a function', }
If I remove the res object to {} the error is res.status is not a function
Am I doing something wrong with the initialization or is my res object wrong?
I now found the following solution:
// given
const func = t.context.authenticationMocked;
t.context.authenticateStub.withArgs(sinon.match.any, sinon.match.any, sinon.match.any).yields('error', { statusCode: 500 }, 'sampleUser').returns(() => {});
const nextSpy = sinon.spy();
const fakeReq = { user: { email: '' } };
const rootRouteStub = {
status: sinon.stub(),
json: sinon.spy(),
};
rootRouteStub.status.returns(rootRouteStub);
// when
func(fakeReq, rootRouteStub, nextSpy);

Categories