I am trying to understand why a console.log statement will not output to the terminal after a successful asynchronous network request. I understand why it would not execute if the request fails, since the execution would jump to the catch block. However, after completing a successful request I see no trace of the logging statement. I have the following code:
function processRequest (url, res) {
return axios.get(url).then(response => {
res.send({status: 'PASS', message: `${response.status}, on ${url}`});
});
}
app.post('/api', async (req, res) => {
let response;
try {
response = await processRequest(someValidURL, res);
console.log('after request'); //this statement does not show up after successful request
} catch (error) {
console.error(error);
}
}
In order to minimize this post, I did not include the require statements for express and the axios libraries as well as the express set-up code. Any help is appreciated.
That is because you are returning a response from within processRequest which means your request processing is finished there and the response is sent.
To achieve what you are trying to do, you should do it like this
function processRequest (url) {
return axios.get(url);
}
app.post('/api', async (req, res) => {
let response;
try {
response = await processRequest(someValidURL);
console.log('after request', response);
res.send({status: 'PASS', message: `${response.status}, on ${url}`});
} catch (error) {
console.error(error);
res.send({status: 'ERROR'});
}
}
Related
I am trying to implement a logout endpoint for my app using passport js. I have already setup register, login and session functionality to the app. All works fine but when I create an endpoint and call req.logout() the endpoint runs and just completely skips the passport logout() function like its not even there, doesn't even throw an error from the callback it just appears to be completely disregarding the entire block of code. I can however confirm the endpoint is definitely being hit. I've tested this with postman and the same problem persists as well.
I have button placed at the top of the page which links to a logout function that calls the login endpoint on my node backend server.
Here is the frontend function
async function logOut() {
await axios({method: "POST", url: "http://localhost:3000/restaurants/logout", withCredentials: true}).then((response) => {
console.log("response status", response)
if (response.status === 200) {
// setCount("login")
}
})
}
Here is the endpoint on my backend server (when calling this endpoint the call comes through from the frontend but just completely ignores the logout method and callback written into the endpoint)
router.post("/logout", function(req, res, next) {
req.logout(function(err) {
if (err) {
return next(err);
}
res.json("logged out");
});
console.log("logout called")
});
I've tried changing req.logout() to req. logOut() I've tried tried req.session.destroy(), tried to change it from a post request to a get request and the same issue persists. Passport docs arent much use as I've implemented the solution exactly as it is described in the docs to no avail.
Hoping someone can help here :)
use res.redirect('/');
Also it's not a good approach to use async await with .then() .catch()
router.post("/logout", function(req, res, next) {
req.logout(function(err) {
if (err) {
return next(err);
}
res.redirect('/');
});
console.log("logout called")
})
change the request like the below
async function logOut() {
try {
const response = await axios({ method: "POST", url: "http://localhost:3000/restaurants/logout", withCredentials: true })
console.log(response)
} catch (error) {
console.log(error)
}
}
Ok so I've figured it out, see working code with comments
// I had to make the route asynchronous on the server side.
router.post("/logout", async function (req, res, next) {
console.log("logout user", req.user)
try {
//I had to include the user before the callback when calling the logout function
req.logOut(req.user, function (err) {
console.log("logout callback called")
if (err) {
console.log("error", err)
return next(err);
}
//removed the json response in the callback as for some reason the callback wont execute the code placed inside it, I can however confirm the session has been scrapped and the user logged out. I also removed the console log for "logout callback called" as it was not firing either.
});
} catch (e) {
console.log(e)
}
//added a response to the frontend with the result of an authentication check
res.json(req.isAuthenticated())
console.log("logout called")
});
I also removed the async/await from the frontends request and just left the standard Javascript promise in.
function logOut() {
axios({method: "POST", url: "http://localhost:3000/restaurants/logout", withCredentials: true}).then((response) => {
console.log("response status", response)
if (response.status === 200) {
// setCount("login")
}
})
}
Having this script:
const got = require("got");
const getStream = require("get-stream");
const app = require("express")();
async function httpGet() {
console.log("getting response");
let targetUrl = "http://localhost:3000/api";
let gotOptions = {
method: "get",
headers: { "content-type": "application/json" },
body: undefined,
retries: 0
};
let response = await new Promise(async (resolve, reject) => {
let stream = got.stream(targetUrl, gotOptions);
stream.on("error", async error => {
try {
resolve(stream);
} catch (err) {
reject(err);
}
});
stream.on("response", async res => {
try {
resolve(await getStream(res));
} catch (err) {
reject(err);
}
})
});
return response;
}
async function apiMiddleware(req, res, next) {
try {
const response = await httpGet();
console.log(response);
} catch (e) {
throw new Error("some error");
}
next();
}
app.use("/", [apiMiddleware]);
app.get("/api", (req, res) => {
res.json({ data: "some output" });
});
app.get("/someapp", (req, res) => {
res.end();
});
app.listen(3000, () => {
console.log("listening on port 3000");
});
Will (upon visiting "localhost:3000/someapp") console.log
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
...
When debugging, the promise will always throw me at the start of httpGet(), so what is going on?
what is going on?
You've registered the apiMiddleware for all routes (/), that includes the /api route. So whenever you are requesting anything from the server, apiMiddleware() is getting called, which will - wait for it - make a http request to http://localhost:3000/api! This leads to an infinite recursion. And, since you're waiting for the response before sending a response, probably also to quickly running out of memory.
Im trying to query a MySQL database and see if a record exists in a table
if it does then render page without inserting to a table
if it does not then call MySQL with another query to write to a table and then render page
What I believe is happening is that the first connection.query runs and before it renders the page when the record exists it tries to insert to table and errors with the below, maybe due to trying to render at the same time but not sure? Any help on solving this will be appreciated.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (_http_outgoing.js:558:11)
exports.follow = async (req, res) => {
try {
pool.getConnection(function (error, connection) {
if (error) {
console.log(error);
return;
}
connection.query(checkExists, async (error, results) => {
if (error)
throw error;
return res.status(200).render('search', {
});
})
connection.query(insertIfDoesNotExist, async (error, results) => {
if (error) throw error;
if (loggedin) {
return res.status(200).render('search', {
});
}
})
}
})
} catch (error) {
console.log(error);
}
}
You're right, connection.query() is asynchronous, so you've end up with race condition. checkExists and insertIfDoesNotExist will be queried synchronously, but it will only run its callback when it gets a reply from the database (this is the async part).
So most probably, you end up calling both call back, and trying to res.render twice, which is not correct. Each HTTP request can only have one response.
So how to solve this? You should nest your callback or use await (if you use a promise version of SQL driver) to something like this
exports.follow = async (req, res) => {
try {
pool.getConnection(function (error, connection) {
if (error) {
console.log(error);
return;
}
connection.query(checkExists, async (error, results) => {
if (error) throw error;
if (!results) // condition to check if it exists here!
// Only insert this after you've confirmed that it does not exists
connection.query(insertIfDoesNotExist, async (error, results) => {
if (error) throw error;
if (loggedin) {
return res.status(200).render('search', {});
}
});
return res.status(200).render('search', {});
});
});
} catch (error) {
console.log(error);
}
};
I have this error handler that retreives specific error messages based on what happens. But the thing is when I run my error handler function with .catch() it will work if i'm logging to the node console, but when i try send it to the client via res.json() it will only send the status code, not any part of my error handler.
function errorHandler(error){
if (error.name === 'SequelizeValidationError') {
const errors = error.errors.map(err => err.message);
return errors;
} else {
throw error;
}
}
router.post('/create', async(req, res) => {
await Movie.create(req.body)
.then(() => res.json("Movie Created"))
.catch( err => res.status(401).json(errorHandler(err)) );
});
This is my code for the error handler and the route i'm talking about. It works in the node console, but like I said it only sends the status 401 code to the client and nothing else. How can I get my error message send to the client as well?
Thank you!
Because its not waiting for result from errorHandler. Make them wait for it.
Try this.
function errorHandler(error, cb){
if (error.name === 'SequelizeValidationError') {
const errors = error.errors.map(err => err.message);
cb(errors);
} else {
throw error;
}
}
router.post('/create', async(req, res) => {
await Movie.create(req.body)
.then(() => res.json("Movie Created"))
.catch( err => {
errorHandler(err, function(errors){
res.status(401).json(errors);
});
});
})
Or you can return a Promise and await on errorHandler.
I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)