0

I have a doubt related to async function on Express. I'm fetching some JSON from database and I'm getting the following error: Error: Can't set headers after they are sent.

I've read a little bit about it and it seems that it's related to the function having a Finished state or something like that.

I had the following code and it was crashing my application:

 router.get('/home', isAuthenticated, function(req, res){

    atendimentos.find({}, function(err, atendimentos) {
        if(!err){
            console.log(atendimentos);
            return res.json(atendimentos);
        }
    })


    res.render('home', {user: req.user}); 
})

But then I changed to this code:

 router.get('/home', isAuthenticated, function(req, res){

    //Added async to the function
    atendimentos.find({}, async function(err, atendimentos) {
        if(!err){
            console.log(atendimentos);
            return res.json(atendimentos);
        }
    })


    res.render('home', {user: req.user}); 
})

The application stopped crashing but the error continues. I don't know why. Maybe I could get some help handling this?

Thanks

3
  • Can you try .then(function() { res.render('home', {user: req.user}); (right after the parenthesis closing args of find method)? Commented Jul 27, 2018 at 14:36
  • What does your route controller do? Return some json formatted data or render a html view? You "can't" do both (you may return a html view with the json as context data) Commented Jul 27, 2018 at 14:41
  • Yeah, it makes sense, I have to do just ONE thing, I render the page OR I send the data. I'll try to separate the routes or something like that, thank you all for helping me. Commented Jul 27, 2018 at 14:44

2 Answers 2

6

res.json() sends the response (formatted as JSON).

res.render() sends the response (using other formatting methods).

You can't send two responses to the same request.

Pick one.

Sign up to request clarification or add additional context in comments.

2 Comments

Okay, that makes sense. But how do I render the view then? I need to fetch the data to this /home route. It's a login page that will render a chart in the future, that's why I'm making the JSON request on the route. Thanks anyway, I will check it out.
@danibrum — Either (1) Put the data into your template engine and use it to render the page or (2) Fetch the JSON (with Ajax) from a different URL
1

From the Docs:

res.render

Renders a view and sends the rendered HTML string to the client.

res.json

Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a JSON string using JSON.stringify().

So, as mentioned, you are sending two responses. If what you want is to have the atendimentos data in your view, you could pass it as a property of the object to res.render, something like:

 router.get('/home', isAuthenticated, function(req, res){

    //Added async to the function
    atendimentos.find({}, async function(err, atendimentos) {
        if(!err){
            console.log(atendimentos);

            res.render('home', {user: req.user, atendimentos}); 
        }
    });

});

Or you could just make a separate API call with fetch, XMLHttpRequest or any other way.

P.S. async has nothing to do with your issue, the reason it stopped crashing is that unhandled rejections do not make Node crash, this will change in a future release.

1 Comment

I'll check that as well. When I pass the data as you said {user: req.user, atendimentos}, the atendimentos will be in JSON already because mongoDB uses JSON on its documents I guess, so this can be a solution too. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.