node js thunder when I send multiple requests to the server at the same time, but not when I send one by one. And nodejs says.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
(node:4) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
That error in common when you send two responses to the client but in my code only sent one
This is my code
const User = require('../../../modelos/users');
async function getAllValuationsByUserName(req, res, next) {
let isOwner = false;
const userId = req.params.id;
const pagination = {
skip: Number(req.query.skip),
limit: Number(req.query.limit)
}
if (userId === res.locals.userid) {
isOwner = true;
}
try {
const user = await User.findOne(
{ userName: new RegExp('^' + userId + '$', "i") },
{
'userPhoto.valuations': {
$slice: [pagination.skip, pagination.limit]
}
})
const valuations = user.userPhoto.valuations
const total = user.userPhoto.valuations.length
return res.status(200).send({
erro: false,
isOwner,
valuations,
total
})
} catch (err) {
console.log(err)
return res.status(400).send({
error: true,
inf: err
});
}
}
module.exports = getAllValuationsByUserName
Related
I have a back-end using firebase-admin and express to allow post requests from the client to the server to make changes to the firestore I have that contains stuff like user data (this is a test and not a real product). I want to check if a document already exists so a user cannot register with that username again. I have first seen instances of doc.exists but that returns undefined for me and I looked into the documentation and found doc.empty which is said to check if a document is empty. I tried it but it returned a promise rejection error. If I changed that line to .exists or to something else, that goes away so I have narrowed down the issue to that line.
index.js (backend)
app.post("/registeruser", function (req, res) {
res.setHeader("Content-Type", "application/json");
try {
const username = req.body.username;
const password = req.body.password;
const passwordEncrypted = HmacSHA1(password, JSON.parse(fs.readFileSync("./keys.json"))["passwordEncryptKey"]).toString();
// console.log(username, password, passwordEncrypted);
try {
firestore.collection("users").get(username).then(function (data) {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
}).catch(function (error) {
throw [true, error];
});
if (username == "") {
firestore.collection("users").add({
username: v4(),
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}))
}).catch(function (error) {
throw [true, error];
});
}
else {
firestore.collection("users").doc(username).set({
username: username,
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}));
}).catch(function (error) {
throw [true, error];
});
}
}
catch (error) {
throw [true, error];
}
}
catch (error) {
console.log(error);
const [isError, errorMessage] = error;
return res.status(404).send(JSON.stringify({
error: isError,
message: errorMessage,
}));
}
});
Terminal Output
(node:29448) UnhandledPromiseRejectionWarning: [object Array]
(node:29448) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:29448) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
You have multiple concurrent promise chains, and some of those can fail independently. You need to consolidate all your logic into one promise chain.
return firestore.collection("users").get(username)
.then((data) => {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
})
.then(() => {
if (username == '') {
return firestore.collection("users").add({/* Your data */});
}
return firestore.collection("users").doc(username).set({/* Your data */});
})
.then(() => {
return res.status(200);
})
.catch((err) => {
return res.status(500);
});
You can also try using async/await which will significantly simplify logic like this.
New to node- I'm trying to save some of my tweets from Twitter API into mongo using Twit package.
I've connected to mongodb on port 27017 using mongoose, and this piece of code I've written seems to save the tweets to my db, however I seem to be getting this warning back everytime I save a document:
(node:9991) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 8)
Here is my code:
const Tweet = require('./app/models/tweet.model.js');
const dbConfig = require('./config/database.config.js');
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.url, {
useNewUrlParser: true
}).then(() => {
console.log("Successfully connected to the database");
}).catch(err => {
console.log('Could not connect to the database. Exiting now...', err);
process.exit();
});
var Twit = require("twit");
var config = require("./config/twitter.config");
var T = new Twit(config);
var params = {
screen_name: "decade3uk",
count: 2
};
T.get("statuses/user_timeline", params, gotData);
function gotData(err, data, response) {
var tweets = data;
for(var i=0;i<tweets.length;i++){
const tweet = new Tweet({
created_at:tweets[i].created_at,
id_str:tweets[i].id_str,
text:tweets[i].text
});
tweet.save()
.then(entry => {
response.send(entry);
}).catch(err => {
response.status(500).send({
message: err.message || "Some error occurred while creating the Tweet."
});
});
}
}
What is best practice to get rid of this error?
Why don't you try to find where is that exception coming from and what exactly it is. You can find that by adding the following code to your server file, just to make sure you get what's causing the exception.
process.on('unhandledRejection', (reason, promise) => {
console.log("Reason: ",reason,"promise: ",promise);
})
Node.js v10.15.2, googleapis v40.0.0
I tried this
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
},
{
responseType: 'stream'
}, (err, res) => {
res.data.on('end', () => {
console.log('Done downloading file!');
})
.on('error', console.error)
.pipe(dest);
});
And got error
$ node .
Files:
20140810_125633.mp4 (1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE)
Getting started (0B3K2QXOGSOFRc3RhcnRlcl9maWxl)
(node:62335) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined
at drive.files.get (/Users/user/Development/app/google_drive_integration/index.js:104:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62335) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:62335) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:62335) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined
at drive.files.get (/Users/user/Development/app/google_drive_integration/index.js:104:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62335) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Also, I tried this example from Google API reference.
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
And got error
$ node .
Files:
20140810_125633.mp4 (1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE)
(node:62336) UnhandledPromiseRejectionWarning: TypeError: drive.files.get(...).on is not a function
at downloadFile1 (/Users/user/Development/app/google_drive_integration/index.js:118:8)
at files.map (/Users/user/Development/app/google_drive_integration/index.js:87:9)
at Array.map (<anonymous>)
at drive.files.list (/Users/user/Development/app/google_drive_integration/index.js:84:13)
at createAPIRequestAsync.then.r (/Users/user/Development/app/google_drive_integration/node_modules/googleapis-common/build/src/apirequest.js:48:53)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62336) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:62336) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:62336) UnhandledPromiseRejectionWarning: Error: The user has not granted the app 619229308650 read access to the file 1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE.
at Gaxios.<anonymous> (/Users/user/Development/app/google_drive_integration/node_modules/gaxios/build/src/gaxios.js:73:27)
at Generator.next (<anonymous>)
at fulfilled (/Users/user/Development/app/google_drive_integration/node_modules/gaxios/build/src/gaxios.js:16:58)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62336) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
I list file 20140810_125633.mp4 successfully but can't download it. What am I doing wrong?
All the code I have
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
authorize(JSON.parse(content), listFiles);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
/**
* Lists the names and IDs of up to 10 files.
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listFiles(auth) {
const drive = google.drive({version: 'v3', auth});
drive.files.list({
q: "mimeType != 'application/vnd.google-apps.folder'",
pageSize: 1000,
fields: 'nextPageToken, files(id, name)',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const files = res.data.files;
if (files.length) {
console.log('Files:');
files.map((file) => {
console.log(`${file.name} (${file.id})`);
downloadFile0(drive, file.id);
downloadFile1(drive, file.id);
});
} else {
console.log('No files found.');
}
});
}
function downloadFile0(drive, fileId) {
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
},
{
responseType: 'stream'
}, (err, res) => {
res.data.on('end', () => {
console.log('Done downloading file!');
})
.on('error', console.error)
.pipe(dest);
});
}
function downloadFile1(drive, fileId) {
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
}
In API v3 you get a promise from "drive.files.get" and on the promise resolve you have the "data" stream
function downloadFile(drive, fileId, fileName) {
const filePath = `/Users/user/Downloads/${fileName}`;
const dest = fs.createWriteStream(filePath);
let progress = 0;
drive.files.get(
{ fileId, alt: 'media' },
{ responseType: 'stream' }
).then(res => {
res.data
.on('end', () => {
console.log('Done downloading file.');
})
.on('error', err => {
console.error('Error downloading file.');
})
.on('data', d => {
progress += d.length;
if (process.stdout.isTTY) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`Downloaded ${progress} bytes`);
}
})
.pipe(dest);
});
}
When trying to authenticate users with Mongoose I get the following warning in my console:
(node:20114) UnhandledPromiseRejectionWarning: undefined
(node:20114) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:20114) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Trying to trace the stack doesn't result in anything, all I get is undefined. Similar questions exist here on Stackoverflow, but they don't apply to my situation. Any idea what this could cause?
My route controller is calling the findByCredentials function inside the Mongoose model:
Controller
static login(req, res) {
User.findByCredentials(req.body.email, req.body.password)
.then(user => {
return user.generateAuthToken().then(token => {
res.header("x-auth", token).json(user);
});
})
.catch(error => {
res.status(400).json({ message: "Invalid credentials." });
});
}
Model
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
Promise.reject();
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};
The error undefined is coming from your Promise.reject() you are not passing any error message to it, so you are literally throwing undefined.
It is not caught in the catch in login, because you are not returning it from your findByCredentials method.
Solution:
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
return Promise.reject('User not available');
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};
I tried to write this code very simple to focus on solving the problem, but what I really want is for one nodejs controller to call another more advanced nodejs controller multiple times.
This is my Route
// test Route
var express = require('express');
var router = express.Router();
var testController = require('../controllers/testController');
router.get('/getTest', function(req, res) {
testController.getTest(req, res);
});
module.exports = router;
This is my testController.js
exports.getTest = function(req, res) {
var myArray = [300,200,400,100,500];
var myResult = [];
// Find all data based on myArrau
var findData = function() {
return new Promise((resolve, reject) => {
for (var i=0; i<myArray.length; i++) {
callDataController(i, myArray[i]);
}
resolve();
})};
// Call and get the specific data
var dataController = require('./dataController');
var callDataController = function(i, myValue) {
return new Promise((resolve, reject) => {
dataController.getData (
{ "myValue": myValue }, res,
function(err, data) {
if (!err) {
myResult[i] = data;
resolve(data);
} else {
reject(new Error('ERR dataController: ' + err));
};
});
})};
// Send result to page
var sendResult = function(data) {
return new Promise((resolve, reject) => {
res.json({error:false, "myResult":myResult})
resolve();
});
};
// Run promises
findData()
.then(sendResult)
.catch(err => {
console.log("getTest ERR: " + err);
res.json({error:true,err})
}
)
}
And this is my dataController which usually do a lot of mongodb work
exports.getData = function(req, res) {
console.log("Data received: " + JSON.stringify(req, null, 4));
console.log("Doing lots of mongodb work taking milliseconds to minutes")
setTimeout(function(){},req.myValue);
res.json({error:false, "myValue":req.myValue+1000 })
}
And here comes the problem, best explained by the terminal console.log
Data received: {
"myValue": 300
}
Do a LOT of mongodb work taking anywhere from milliseconds to minutes
Data received: {
"myValue": 200
}
Do a LOT of mongodb work taking anywhere from milliseconds to minutes
Data received: {
"myValue": 400
}
Do a LOT of mongodb work taking anywhere from milliseconds to minutes
Data received: {
"myValue": 100
}
Do a LOT of mongodb work taking anywhere from milliseconds to minutes
Data received: {
"myValue": 500
}
Do a LOT of mongodb work taking anywhere from milliseconds to minutes
GET /testRoute/getTest 304 8.448 ms - -
getTest ERR: Error: Can't set headers after they are sent.
(node:9976) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
(node:9976) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Can't set headers after they are sent.
(node:9976) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): Error: Can't set headers after they are sent.
(node:9976) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 4): Error: Can't set headers after they are sent.
(node:9976) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 6): Error: Can't set headers after they are sent.
Even if I only put one value in myArray I still get the Error: Can't set headers after they are sent.
This is the one point of async which I just cant get to work.
(already tried adding them all to a Promise.all array, same problem)
Your problem is that the controller that you're calling multiple times is writing the response to the client - multiple times (or at least, it's trying to). Instead, let it just return a promise, and respond only in the router code:
// test Route
var express = require('express');
var router = express.Router();
var testController = require('../controllers/testController');
router.get('/getTest', function(req, res) {
testController.getTest(req).then(function sendResult(myResult) {
res.json({error:false, "myResult":myResult}))
}, err => {
console.log("getTest ERR: " + err);
res.json({error:true,err});
});
});
module.exports = router;
// testController.js
var dataController = require('./dataController');
exports.getTest = function(req) {
var myArray = [300,200,400,100,500];
var myResult = [];
// Find all data based on myArrau
function findData() {
return myArray.map(callDataController);
}
// Call and get the specific data
function callDataController(myValue, i) {
return dataController.getData (
{ "myValue": myValue }
).catch(function(err) {
throw new Error('ERR dataController: ' + err)
});
};
// Run promises
return Promise.all(findData());
}
// dataController
exports.getData = function(req) {
return new Promise((resolve, reject) => {
console.log("Data received: " + JSON.stringify(req, null, 4));
console.log("Doing lots of mongodb work taking milliseconds to minutes")
setTimeout(function(){
resolve({error:false, "myValue":req.myValue+1000 });
}, req.myValue);
});
};