cannot set headers after they are sent to the client - mongoose - node.js

I want to write a small app that receives a welcome message via POST and returns it via GET. If I call only one method (GET or POST), there are no problems, but as soon as I call GET and POST, I get the following message:
events.js:174
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (C:\Users\aph\IdeaProjects\hello-world-node\ExpressApp\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\aph\IdeaProjects\hello-world-node\ExpressApp\node_modules\express\lib\response.js:170:12)
at Greeting.find (C:\Users\aph\IdeaProjects\hello-world-node\ExpressApp\routes\hello.js:16:13)
at C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\model.js:4568:16
at C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\query.js:4315:12
at process.nextTick (C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\helpers\query\completeMany.js:35:39)
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\model.js:4570:13
at C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\query.js:4315:12
at process.nextTick (C:\Users\aph\IdeaProjects\hello-world-node\node_modules\mongoose\lib\helpers\query\completeMany.js:35:39)
at process._tickCallback (internal/process/next_tick.js:61:11)
This is my code:
const express = require("express");
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const router = express.Router();
const Greeting = mongoose.model("Greeting", new Schema({message: String}));
router.get("/", (req, res) => {
Greeting.find({message: "Hello World!"}, (err, greetings) => {
if (err) {
console.log(err);
res.status(500).send(err);
return;
}
res.send(JSON.stringify(greetings));
});
res.send("There are no greetings!");
});
router.post('/', (req, res) => {
mongoose.connect("mongodb://localhost:27017/test", {useNewUrlParser: true});
new Greeting(req.body).save()
.then(() => {
res.send('success');
})
.catch(err => {
console.log(err);
res.status(500).send("Error: " + err)
});
});
module.exports = router;
I scanned this question but could not find a solution for my problem.

Greeting.find is an async function so the line res.send("There are no greetings!"); runs before the callback of Greeting.find runs which means the response 'There are no greetings!' is sent to the client before the callback ever runs.
Then in Greeting.find callback you're trying to send a response to the client again which causes the error.

Related

Unable to access protected routes using JWT in NodejJS

I am trying to protect application routes using jwt.I am able to generate jwt and to validate jwt I have created middleware authorize.js which I am passing to /sec route in below code but when I am trying to access protected routes using jwt it showing below error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:518:11)
at ServerResponse.header (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:267:15)
at D:\Backend\NodeJs\aws_test\server.js:24:9
at Layer.handle [as handle_request] (D:\Backend\NodeJs\aws_test\node_modules\express\lib\router\layer.js:95:5)
at next (D:\Backend\NodeJs\aws_test\node_modules\express\lib\router\route.js:137:13)
at D:\Backend\NodeJs\aws_test\auth.js:21:20
at Object.module.exports [as verify] (D:\Backend\NodeJs\aws_test\node_modules\jsonwebtoken\verify.js:53:12)
at authorize (D:\Backend\NodeJs\aws_test\auth.js:11:13)
Below is how I am setting jwt in POSTMAN:
Below is my code:
server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const authorize = require('./auth');
const chalk = require('chalk');
const app = express();
const port = 3000 || process.env.PORT;
app.get('/',(req,res) => {
res.send("Home page");
});
app.get('/jwt',(req,res) => {
let token = jwt.sign({"body":"stuff"},"mypassphrase",{ algorithm: 'HS256'});
console.log(chalk.blue(token));
});
app.get('/sec',authorize,(req,res) => {
res.json({"name":"Hello digi"});
});
app.listen(port,(req,res) => {
console.log(chalk.green(`App is running at ${port}`));
});
auth.js
const fs = require('fs');
const jwt = require('jsonwebtoken');
authorize = (req,res,next) => {
if(typeof req.headers.authorization !== "undefined"){
let token = req.headers.authorization.split(" ")[1];
let key = fs.readFileSync('./private.pem','utf-8');
jwt.verify(token, key,{ algorithm: "HS256" },(err,user) => {
if (err) {
// shut them out!
res.json({ error: "Not Authorized" });
// throw new Error("Not Authorized");
}
// if the JWT is valid, allow them to hit
// the intended endpoint
return next();
});
}
else{
// No authorization header exists on the incoming
// request, return not authorized and throw a new error
res.json({ error: "No Authorization header" });
// throw new Error("Not Authorized");
}
}
module.exports = authorize;
What I am doing wrong in above code or what need to be correct.
The only execution path that looks to be liable to cause the error you're seeing is the following - in the autorize middleware:
if (err) {
res.json({ error: "Not Authorized" }); // you're not returning here
}
return next();
In case of an error happening here, you're not returning execution to the calling code when sending back a response to the client, so the current request is fowarded to the next middelware in line - which will end up resulting in another response being sent.
As for the reason why this path is being executed in the first place, judging from your Postman screen grab, the value you're setting for the Authorization header looks it may be incorrect - you're setting the value to jwt, and the description to the actual token, while you would actually want to set the value to the combination of both separated by a space like jwt token.

PATCH route/endpoint in Express not working

Hi I've written the following route for an api endpoint which isn't working. When I test with Postman and my code, it's simply a 404 not found error.
router.patch("/favorite", async (req, res) => {
user = await User.findById(req.body.id)
if (user == null) {
return res.status(404).json({ message: 'Cannot find user' })
}
if (req.body.putArr != null) {
res.user.favPokemon = req.body.putArr;
}
try {
const updatedUser = await res.user.save();
console.log(res.user.favPokemon);
console.log(updateUser);
res.json(updatedUser);
} catch (err) {
res.status(400).json({ error: err.message });
}
});
What am I missing/what error do I have in my code? For reference, here's my mongoDB setup for users:
Edit: Apologies for not specifying the endpoint. To be more clear, the end point and code calling this is:
const favThis = async (e) => { // Patch method to favorite or unfavorite a pokemon
debugger;
e.preventDefault();
try {
console.log(putArr);
const newUser = {userID, putArr};
await axios.patch("http://localhost:5000/users/favorite", newUser);
} catch(err) {
err.response.data.msg && setError(err.response.data.msg)
}
};
, so it's http://localhost:5000/users/favorite. I have other endpoints working fine such as http://localhost:5000/users/login and http://localhost:5000/users/register, and inside server.js I have app.use("/users", require("./routes/users"));
Additionally, server.js is simply
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
require("dotenv").config();
// set up express
const app = express();
app.use(express.json());
app.use(cors());
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`The server has started on port: ${PORT}`));
// set up mongoose
mongoose.connect(
process.env.MONGODB_CONNECTION_STRING,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
},
(err) => {
if (err) throw err;
console.log("MongoDB connection established");
}
);
// set up routes
app.use("/users", require("./routes/users"));
app.use("/todos", require("./routes/todo"));
Edit 2:: I notice now that when I test on Postman, it's an infinite loop and the call is hung. I also get the following warnings in my console:
(node:36447) 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)
and
(node:36447) [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.
Based on the warning you have failing code inside a promise which is not being caught. Perhaps in this line user = await User.findById(req.body.id).
Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai

Problem with handling errors in Express framework

I am currently working on node.js + express + mongoDB project. I am trying to handle error that occurs when data cannot be received from database. I am simulating this by terminating mongod process in console and calling .get in Postman. Sadly instead of getting an error in Postman I only get Unhandled Promise Rejection in console. I read a lot of posts about error handling and implemented it according to this guide: https://expressjs.com/en/guide/error-handling.html. I would be grateful for any idea of how can I fix this.
The code:
Printing all courses:
router.get("/", async (req, res, next) => {
try {
const courses = await Course.find().sort("dishName");
res.send(courses);
} catch (ex) {
next(ex);
}
});
error.js:
module.exports = function (err, res, req, next) {
res.status(500).send(`500 Error`);
};
index.js
const error = require(`./middleware/error`);
app.use(error);
app.use(error) is placed as the last app.use
There is a minor mistake in your code. The order of the req and res parameters in the error handler function should not be changed.
// Error.js
module.exports = function (err, req, res, next) {
res.status(500).send(`500 Error`);
};

ExpressJS 4 execute function after res.send()

I am new to nodeJS and asynchronous programming. I am using express as the base for my app, there is really only one route that serves a page and accepts an upload from a form. I would like to make a POST request to an external service after the the file has been uploaded. Attempting to execute any code after res.send(200) however results in an error message of Error: Can't set headers after they are sent.
I am using the request package to make the post to the external service.
var express = require('express');
var router = express.Router();
var util = require("util");
var request = require("request");
/* POST uploads. */
router.post('/', function(req, res, next) {
console.log("LOG:" + util.inspect(req.files));
res.send('respond with a resource');
postFile(req.files.file.path);
});
var postFile = function(path) {
var opts = {
method: 'POST',
uri: 'https://example.com/api/files.upload',
formData: {
token: "xxx",
file: fs.createReadStream(req.files.file.path)
}
}
// console.log("LOG:\n" + util.inspect(opts));
request.post(opts, function(error, response, body) {
if (error) {
console.log("ERROR LOG: " + util.inspect(error));
} else {
console.log("RESPONSE LOG:" + util.inspect(response));
}
});
}
module.exports = router;
The postFile function works fine on it's own and even adding a console.log directly after the res.send results in the same error. How can I continue to execute code on the server after the response has been sent to the client?
Output log from node:
POST /uploads 200 85.201 ms - 23
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:154:12)
at fn (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:934:10)
at View.exports.renderFile [as engine] (/Users/jason/dev/test/file-share/node_modules/jade/lib/index.js:374:12)
at View.render (/Users/jason/dev/test/file-share/node_modules/express/lib/view.js:93:8)
at EventEmitter.app.render (/Users/jason/dev/test/file-share/node_modules/express/lib/application.js:566:10)
at ServerResponse.res.render (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:938:7)
at /Users/jason/dev/test/file-share/app.js:58:7
at Layer.handle_error (/Users/jason/dev/test/file-share/node_modules/express/lib/router/layer.js:58:5)
There's nothing wrong in the code below, you can execute code after responding the request. You just can't send headers again.
router.post('/', function(req, res, next) {
console.log("LOG:" + util.inspect(req.files));
res.send('respond with a resource');
postFile(req.files.file.path);
});
Using the request to POST or GET something will not respond to your request, unless you want to.
Your postFile() function is referring to req.files.file.path in this line:
file: fs.createReadStream(req.files.file.path)
but the req object is not being passed to it. This should generate some sort of exception.
It appears you should be using just:
file: fs.createReadStream(path)
since you are passing the path to postFile(path).

express error middleware not handling errors thrown from promise.done()

If I throw an Error, express renders it nicely using the connect errorHandler middleware.
exports.list = function(req, res){
throw new Error('asdf');
res.send("doesn't get here because Error is thrown synchronously");
};
And when I throw an Error within a promise it will be ignored (this makes sense to me).
exports.list = function(req, res){
Q = require('q');
Q.fcall(function(){
throw new Error('asdf');
});
res.send("we get here because our exception was thrown async");
};
However, If I throw an Error within a promise and call "done" node crashes, because the exception isn't caught by the middleware.
exports.list = function(req, res){
Q = require('q');
Q.fcall(function(){
throw new Error('asdf');
}).done();
res.send("This prints. done() must not be throwing.");
};
After running the above, node crashes with this output:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: asdf
at /path/to/demo/routes/user.js:9:11
So my conclusion is that done() isn't throwing the exception, but causes an exception to be thrown elsewhere. Is that right? Is there a way to accomplish what I'm trying - where errors in the promise will be handled by the middleware?
FYI: This hack will catch the exception at the top level, but it's outside of the realm of middleware, so doesn't suite my needs (to nicely render the error).
//in app.js #configure
process.on('uncaughtException', function(error) {
console.log('uncaught expection: ' + error);
})
Maybe you'll find connect-domain middleware useful for handling async errors. This middleware allows you to handle async errors like a regular errors.
var
connect = require('connect'),
connectDomain = require('connect-domain');
var app = connect()
.use(connectDomain())
.use(function(req, res){
process.nextTick(function() {
// This async error will be handled by connect-domain middleware
throw new Error('Async error');
res.end('Hello world!');
});
})
.use(function(err, req, res, next) {
res.end(err.message);
});
app.listen(3131);

Resources