Getting the error 'Cannot set headers after they are sent to the client' in node.js

434 Views Asked by At

I have a frontend and server-backend, where I send a request to the backend for some book-information. The backend then calls an API for this information and returns it to the frontend. It all works fine at the first request. If I do another request from the frontend it yields the error below.

How can I do such that its possible to keep making a new request from the frontend?

node:internal/errors:478
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:387:5)
    at ServerResponse.setHeader (node:_http_outgoing:644:11)
    at ServerResponse.header (/Users/jeff/Documents/myProject/backend/node_modules/express/lib/response.js:794:10)
    at ServerResponse.send (/Users/jeff/Documents/myProject/backend/node_modules/express/lib/response.js:174:12)
    at ServerResponse.json (/Users/jeff/Documents/myProject/backend/node_modules/express/lib/response.js:278:15)
    at file:///Users/jeff/Documents/myProject/backend/controllers/model.js:22:14
    at file:///Users/jeff/Documents/myProject/backend/controllers/model.js:63:10
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 'ERR_HTTP_HEADERS_SENT'
}
[nodemon] app crashed - waiting for file changes before starting...

showBooks is the entry-point for the rest-call from the frontend.

export const showBooks = async (req, res) => {

   const books = req.body.books;

   await getBooksInfo(books, (err, results) => {
      if (err) {
         res.send(err);
      } else {
         res.json(results);
      }
   });
}

getBooksInfo is then called, which loops through the provided books

const getBooksInfo = async (books, result) => {

   let all_books = [];

   books.forEach(async (book) => {

      if (book.type == "adventure") {

         const on_sale = book.meta.sale;

         if (on_sale == true) {

            let book_authors = book.meta.authors;

            try {
               for (const i in book_authors) { 

                  await book_api(book_authors[i].name, (err, results) => {
                     if (err) {
                        console.log(err);
                     } else {
                        all_books.push(results);
                     }
                  });
               }
            } catch (err) {
               console.log(err);
            }
         }
         result(null, { input: all_books });
      }
   });
}

book_api is responsible for the getting the information on the external rest-point

export const book_api = async (author_name, result) => {

     try {
        const response = await axios.get(`https://book-service.com?author=${author_name}`);
  
        const author_info = response.data.author;
     
         let author = {
            "name" : author_info.name,
            "website" : author_info.website,
         };
        result(null, author);
     } catch (err) {
        result(err, null);
     }
  };
2

There are 2 best solutions below

0
On BEST ANSWER

Note that forEach is not designed for asynchronous operations, try to replace books.forEach(async (book) => ... with for (const book of books)...

1
On

Note that where you have console.log(err), also place return type, so that once request is completed it will return some result(true or false).