Search code examples
javascriptasync-awaitfetch-api

Javascript Fetch API with Promises - Passing HTTP response code


I'm using the following code to make HTTP requests to my API:

fetch('api/register', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(jsonObj)
})
    .then(response => {
        responseClone = response.clone();
        if (!response.ok) {
            console.log(`HTTP ERROR STATUS CODE: ${response.status}`);
        }
        return response.json();
    })
    .then(function (data) {
        processDataFromServer(data);
    }, function (rejectionReason) {
        console.log('JSON Parsing Error:', rejectionReason, responseClone);
        responseClone.text()
            .then(function (bodyText) {
                console.log('Cannot parse as JSON:', bodyText);
            });
    })
    .catch(error => {
        console.log("Error: " + error)
        deplayError(error);
    });

The code works! The only change I want to do is to pass response.status to my processDataFromServer(data) function. Something like processDataFromServer(data, response.status). Since response.json() is a promise, I cannot return a promise and a property at the same time to my next method. Any Idea how it can be done?


Solution

  • It will be easier if you rewrite your code as an async function and use await. Something like this:

    async function retrieve() {
        try {
            const response = await fetch('api/register', {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(jsonObj)
            });
            const responseClone = response.clone();
            if (!response.ok) {
                console.log(`HTTP ERROR STATUS CODE: ${response.status}`);
            }
            try {
                const data = await response.json();
                processDataFromServer(data, response.status);
            } catch (rejectionReason) {
                console.log('JSON Parsing Error:', rejectionReason, responseClone);
                const bodyText = await responseClone.text();
                console.log('Cannot parse as JSON:', bodyText);
            }
        } catch (error) {
            console.log("Error: " + error)
            deplayError(error);
        }
    }
    
    retrieve();
    

    Without async

    As you mention in comments you'd like to stick with the .then chain, here is an alternative:

    You can combine the promise response.json() with the response.status in an array and pass that to Promise.all. This will resolve to both the data and the status.

    So change this:

    return response.json();
    

    To:

    return Promise.all([response.json(), response.status]);
    

    And receive the resolved values in the next then callback, by changing this:

    .then(function (data) {
    

    To:

    .then(function ([data, status]) {
    

    ...and now you have access to status for your call to processDataFromServer.