express/node: Cannot set headers after they are sent to the client - javascript

I'm having some issues with my expressJS application, posting to one route will always result in Cannot set headers after they are sent to the client - I don't understand why and where I'm sending a request/response twice.
I tried playing around with async and await in my functions to get rid of this error but ultimately it's always coming back. I'm also writing an ID to a database, I thought this would be the issue. But I don't think so, because I'm basically just returning a code and not even checking the dynamodb.put request in my current function.
async function putNewUrl(inputUrl) {
const newId = await getId();
const short = ShortURL.encode(newId);
const params = {
TableName: URL_TABLE,
Item: {
long: inputUrl,
short,
},
};
try {
const data = await dynamoDb.put(params).promise();
return short;
} catch (error) {
console.log(error);
}
return short;
}
app.post('/submit', async (req, res) => {
const inputUrl = req.body.url;
try {
const shortUrl = await putNewUrl(inputUrl);
console.log(shortUrl)
return res.json({ success: true, message: shortUrl });
} catch(error) {
console.log(error)
return
}
});
here are my imports:
import { config, DynamoDB } from 'aws-sdk';
import { json } from 'body-parser';
import express from 'express';
import helmet from 'helmet';
import { URL } from 'url';
const app = express();
app.use(helmet());
this is how I start my server
app.listen(3000, () => { console.log('app running'); });
solved it:
there was another route like this:
app.post('/submit', (req, res, next) => {
const inputUrl = req.body.url;
findExistingUrl(inputUrl, (error, data) => {
if (error) {
return res.json({ success: false, message: 'server error' });
}
if (typeof data.Items[0] === 'undefined' && data.Items[0] !== null) {
next();
} else {
return res.json({ success: true});
}
});
});
where I was calling next() right at the end again.

solved it:
there was another route like this:
app.post('/submit', (req, res, next) => {
const inputUrl = req.body.url;
findExistingUrl(inputUrl, (error, data) => {
if (error) {
return res.json({ success: false, message: 'server error' });
}
if (typeof data.Items[0] === 'undefined' && data.Items[0] !== null) {
next();
} else {
return res.json({ success: true});
}
});
});
where I was calling next() right at the end again.

Related

How to Fix Headers Already Sent to Client in NodeAPI with Angular

I am getting the following error on my node api, which is really just console logging the request at this point.
router.get('/booksByISBN', checkRole, async (req, res) => {
console.log(req.params)
return res.sendStatus(200);
});
node:internal/errors:484
ErrorCaptureStackTrace(err);
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I believe the issue is because of pre-flight CORS data, but no clue how to fix it.
There is one API call in my Angular 15 application, but upon inspection of the Network tab, I see two api calls are actually being made to my endpoint.
I understand this is because of the CORS options request, but I don't know how to fix it to let the API go through.
CheckRole function
var checkRole = async function CheckRoleAuth (req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
await User.findById(decodedToken.userId)
.then(foundUser => {
if (foundUser) {
if (foundUser.role != null || foundUser.role != '') {
if (foundUser.role.includes('Admin'))
{
req.userData = {
email: decodedToken.email,
id: decodedToken.id
};
next();
} else {
return res.sendStatus(401);
}
} else {
return res.sendStatus(401);
}
}
})
.catch(err => {
return res.sendStatus(401);
});
} catch (error) {
return res.sendStatus(401);
}
}
You are combining async/await with then/catch in your checkRole middlware, so probably both your checkRole middleware and your endpoint handler try to send back the response.
Refactor your checkRole middleware like this:
const checkRole = async function CheckRoleAuth(req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
const user = await User.findById(decodedToken.userId).lean();
if (!user) return res.sendStatus(401);
if (!user?.role?.includes('Admin')) return res.sendStatus(403);
req.userData = { email: decodedToken.email, id: decodedToken.id };
next();
} catch (error) {
return res.sendStatus(401);
}
};

Can not get post result from node.js

I'm trying to get result from node.js api however, whenever i deploy my website, it gets html result like below.
but it works when it's on local. but not on the deployed website.
so i tried to use axios post then it gets 404 error.
and another api is work but when i write new api then it gets error.
this is my node.js
//this post work just fine .
app.post("/insertCodeVisual", async (req, res) => {
const { data } = await req.body;
const visualData = new CodeVisualData({
data: data,
});
try {
visualData.save((err, info) => {
res.send(info._id);
});
console.log("success");
} catch (error) {
console.log(error);
}
});
//but this post is not working
app.post("/api/database", async (req, res) => {
const { host, user, password, port, table, database } = await req.body;
var connection = mysql.createConnection({
host: host,
user: user,
password: password,
port: port,
database: database,
});
try {
connection.connect();
} catch (error) {
res.send([["ERROR_CODE"], [error.code]]);
}
const sql = `SELECT * FROM ${table}`;
connection.query(sql, function (err, results) {
if (err) {
return res.send([
["ERROR"],
[`code : ${err.code}`],
[`errno : ${err.errno}`],
[`sqlMessage : ${err.sqlMessage}`],
]);
} else {
const parse = papa.unparse(results, {
quotes: false, //or array of booleans
quoteChar: '"',
escapeChar: '"',
delimiter: ",",
header: true,
newline: "\r\n",
skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
columns: null, //or array of strings
});
const unparse = papa.parse(parse);
res.send(unparse.data);
}
});
});
const __dirname = path.resolve();
app.use(express.static(path.join(__dirname, "dist")));
app.get("/*", (req, res) => {
res.sendFile(path.join(__dirname, "dist", "index.html"));
});
React.js
this one is working absolutely
const insertData = async () => {
try {
if (confirm("are u sure? ")) {
axios
.post(`${backend}/insertCodeVisual`, {
data: {
client: client,
header: header,
},
})
.then(res => {
setJustSavedDataId(res.data);
});
} else {
return;
}
} catch (error) {
console.log(error);
}
};
this below is not working when i deployed .
const getDatabase = async () => {
const url = `${backend}/api/database`;
const api = await axios.post(url, db[id]);
const data = api.data;
try {
setInfo({ ...info, [id]: data });
} catch (error) {
console.log(error);
}
};
So i wonder what cases make this kind of issue.

Can i use route inside a controller in express js?

So I have something like this in one of my controllers:
module.exports.authToken = (req, res, next) => {
const token = req.cookies.jwt;
//console.log(token);
if (!token) {
return res.sendStatus(403);
}
try {
const data = jwt.verify(token, "secret token");
console.log(data);
req.userId = data.id;
return next();
} catch {
return res.sendStatus(403);
}
};
and it's called by a route:
router.get("/protected", authController.authToken, (req, res) => {
return res.json({ user: { id: req.userId, role: req.userRole } });
});
and I want to get a JSON response of that route in one of my other controllers. I tried some things but none of it worked.
What I would do is abstract the response out to a function for re-use:
// the function will just return the data without writing it to the response
function protectedRoute(req) {
return {user: {id: req.userId, role: req.userRole}};
}
router.get("/protected", authController.authToken, (req, res) => {
// in the actual handler you can return the response
return res.json(protectedRoute(req));
});
// make sure the middleware is still being run
router.get("/other_route", authController.authToken, (req, res) => {
// use the same function to get the response from /protected
const protectedResponse = protectedRoute(req);
// do stuff with it
});

Error: Route.post() requires a callback function but got a [object Promise]

I am creating a REST API with express, folowing are my architecture,a router is calling a controller.but I got this error, please help me
Error: Route.post() requires a callback function but got a [object Promise]
/////// EmailLogin.js middleware Handler
const { validationResult } = require('express-validator');
let wrapRoute = async (req, res, next) => {
try {
// run controllers logic
await fn(req, res, next)
} catch (e) {
// if an exception is raised, do not send any response
// just continue performing the middleware chain
next(e)
}
}
const EmailLogin = wrapRoute(async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
var gtoken = req.body.gtoken;
var gSecretKey = env.secret_key;
if (!gtoken) throw new Error('no token')
const captchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${gSecretKey}&response=${gtoken}`
await axios({
url: captchaURL,
method: 'POST',
headers: {ContentType: 'application/x-www-form-urlencoded'},
}).then(response => {
const gVerifyData = response.data
if (gVerifyData.success === true) {
Users.findOne({'email': req.body.email}).select('+hashPassword +status').exec(function (err, user) {
if(err){
return res.status(500).send({err});
} else if (user) {
validPassword = bcrypt.compareSync(req.body.password, user.hashPassword);
if (!validPassword){
return res.send("wrong-info");
} else if (validPassword && user.status == "active") {
token = jwt.sign({ id: user._id }, env.jwtsecret,
{ expiresIn: "168h" });
res.status(200).send({ token: token, user });
}
} else {
return res.send("wrong-info");
}
}
)
}else {
return res.status(500).send('bot');
}
}).catch(error => {
console.log(error);
});
}
});
function errorHandler (err, req, res, next) {
console.log(err);
// If err has no specified error code, set error code to 'Internal Server Error (500)'
if (!err.statusCode) {
err.statusCode = 500;
}
res.status(err.statusCode).json({
status: false,
error: err.message
});
};
module.exports = {EmailLogin};
I'm trying to call it in my router, like this:
/////// Router.js
const express = require('express');
const router = express.Router();
const { check } = require('express-validator');
const EmailLoginController = require('../controllers/EmailLogin');
var emailLoginValidation = [
check('email').notEmpty().trim().escape().isEmail(),
check('password').notEmpty().isLength({ min: 7 }).withMessage('password is invalid'),
];
router.post('/email-login', emailLoginValidation, EmailLoginController.EmailLogin);
module.exports = router;
/////// App.js
var express = require("express");
var app = express();
const Router = require('./routes/Router');
app.use('/', Router);
app.listen(3000, function() {
console.log('listening on 3000');
});
What could I do ? is it possible to get a Promise Result in the Router as a Handler?
#turkdev Change your email login function to this
const EmailLogin = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
var gtoken = req.body.gtoken;
var gSecretKey = env.secret_key;
if (!gtoken) throw new Error('no token')
const captchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${gSecretKey}&response=${gtoken}`
await axios({
url: captchaURL,
method: 'POST',
headers: { ContentType: 'application/x-www-form-urlencoded' },
}).then(response => {
const gVerifyData = response.data
if (gVerifyData.success === true) {
Users.findOne({ 'email': req.body.email }).select('+hashPassword +status').exec(function (err, user) {
if (err) {
return res.status(500).send({ err });
} else if (user) {
validPassword = bcrypt.compareSync(req.body.password, user.hashPassword);
if (!validPassword) {
return res.send("wrong-info");
} else if (validPassword && user.status == "active") {
token = jwt.sign({ id: user._id }, env.jwtsecret,
{ expiresIn: "168h" });
res.status(200).send({ token: token, user });
}
} else {
return res.send("wrong-info");
}
}
)
} else {
return res.status(500).send('bot');
}
}).catch(error => {
console.log(error);
});
}
};
The problem was earlier, you were assigning it to method wrapRoute() which returns a Promise, which was not settled, causing the error which you got.
If that was just for calling next() on error, you could always use it in the catch block.

How to fix router.delete() which is not working - Express.js?

I am trying to run a delete request but it is not working, I have used the exact same logic on another project exactly like it and it works there.
Here is the route file which includes the delete request as well as the post request that does indeed work
const express = require("express");
const router = express.Router();
const User = require("../models/users");
const cardSchema = require("../models/card");
//add card request
router.post("/:id/addcard", getUser, async (req, res) => {
try {
if (req.body != null) {
const newCard = new cardSchema({
name: req.body.name,
cardNumber: req.body.cardNumber,
ccv: req.body.ccv,
expiration: req.body.expiration,
});
res.user.cardInfo.push(newCard);
}
const updatedCardInfo = await res.user.save();
return res.status(200).json(updatedCardInfo);
} catch (error) {
return res.status(400).json({ message: error.message });
}
});
//delete card request
router.delete("/:id/deletecard", getUser, async (req, res) => {
if (req.body !== null) {
res.user.cardInfo.remove(req.body);
}
try {
const updatedUser = await res.user.save();
res.status(200).json(updatedUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
//get user middleware
async function getUser(req, res, next) {
let user;
try {
user = await User.findById(req.params.id);
if (user == null) {
return res.status(404).json({ message: "Cannot find user" });
}
} catch (error) {
return res.status(500).json({ message: error.message });
}
res.user = user;
next();
}
module.exports = router;
I have triple checked that I am using the correct URL and passing in the correct information in the req.body. I recieved the users information after calling the delete request but just does not remove the card information. I have also checked in my database that it is 'cardInfo' so there is no spelling mistake there either.

Categories

Resources