Search code examples
javascriptnode.jsfetch-api

(Uncaught promise) Failed to fetch while POST'ing different json content


I am trying to GET user listings from a node.js server with the fetch API, by POST'ing the json {pageNumber,UsersPerPage}. For that I have two buttons, that each POST their own text contents as pageNumbers to the server, when clicked. However, after iterating the two buttons 6 times the fetch API somehow fails. It fails after I send 6 fetch requests -so when I iterate between the two buttons six times -and the error message "Uncaught (in promise) TypeError: Failed to fetch at HTMLDivElement." gets returned.

This is my frontend code:


side1.addEventListener('click',()=>{
    fetch('/api/users/stats/side1/post',{  
       method:'POST',
       headers:{'Content-Type':'application/json'},
       body:JSON.stringify({
        pageNum:side1.textContent,
        userPerPage:userPerSide
       })
    
    });
    fetch('/api/users/stats/side1/get').then(x=>x.text()).then(Txt=>{
    display.innerHTML=Txt;
    });
});
side2.addEventListener('click', ()=>{
    fetch('/api/users/stats/side2/post',{
       method:'POST' ,
       headers:{'Content-Type':'application/json'},
       body:JSON.stringify({
        pageNum:side2.textContent,
        userPerPage:userPerSide
       })
    
    });  
    fetch('/api/users/stats/side2/get').then(x=>x.text()).then(Txt=>{
        display.innerHTML=Txt;
        });  
});


This is my backend - code:


var retVal; 
app.post('/api/users/stats/side1/post',(req,res)=>{
if(req.body){
const {pageNum,userPerPage}=req.body;
retVal=GetPage(Number(pageNum),Number(userPerPage));
}
});
app.post('/api/users/stats/side2/post',(req,res)=>{
    if(req.body){
    const {pageNum,userPerPage}=req.body;
    retVal=GetPage(Number(pageNum),Number(userPerPage));
    }
});
app.get('/api/users/stats/side1/get',(req,res)=>{
res.setHeader('content-type', 'text/plain');
res.send(retVal);
});
app.get('/api/users/stats/side2/get',(req,res)=>{
res.setHeader('content-type', 'text/plain');
res.send(retVal);
});


I tried different header flags, like explicitly declaring Access-Control-Allow-Origin or no-cors and also async, await on the front-end. The bug occurs independently of the kind of content that gets returned by the server. I tried simplifying the code and changing the POST and GET urls for different html objects to avoid collisions. I was expecting for the fetch requests to be executed, without any sort of blocking. I already did search through the web as well as stackoverflow and while other questions sound familiar to this one, they are about a completely different problem, I think.

What I learned: The problem does not occur, due to requests taking a bit too much time. And it is not due to Javascript being unable to interact with my server, since the first 5 requests work like expected and then the code breaks. Looking at the network tab, the requests are suddenly pending, so they stop getting through after this point.


Solution

  • First, I think you could reduce your 4 calls to only one.

    app.post('/api/users/stats/post',(req,res)=>{
    if(req.body){
    const {pageNum,userPerPage}=req.body;
    res.send(GetPage(Number(pageNum),Number(userPerPage)));
    }else{
    // Missing body, say that the input is bad.
    res.sendStatus(422);
    }
    });
    

    This alone is enough.

    I have removed the "side1" part as the code is exactly the same as for side2. So let's keep only one route and remove that side thing.

    Then, since the POST was used to "pre-configure" the result of the next GET, the GET is completely superfluous since the POST could simply return the result directly. Removing the need for a whole round-trip. At the same time, this completely removes the problem of having to call post before get and making sure that no one else calls post until I call get.

    Now, the client had a couple mistakes too. First big one is that it was not waiting for the POST call to finish before calling GET. So it would be possible that the GET would receive the previous value instead of the correct one. But we've got this fixed by using only one POST endpoint.

    side1.addEventListener('click',()=>{
        fetch('/api/users/stats/post',{  
           method:'POST',
           headers:{'Content-Type':'application/json'},
           body:JSON.stringify({
            pageNum:side1.textContent,
            userPerPage:userPerSide
           })
        }).then(x=>x.text()).then(Txt=>{
        display.innerHTML=Txt;
        });
    });
    side2.addEventListener('click',()=>{
        fetch('/api/users/stats/post',{  
           method:'POST',
           headers:{'Content-Type':'application/json'},
           body:JSON.stringify({
            pageNum:side2.textContent,
            userPerPage:userPerSide
           })
        }).then(x=>x.text()).then(Txt=>{
        display.innerHTML=Txt;
        });
    });
    

    Now this gets a bit less complicated again.

    There is other code clean-up that we could do like having the same event listener for both objects etc, but I'm leaving it for now to make the answer clearer.