122

I have a react/redux application and I'm trying to do a simple GET request to a sever:

fetch('http://example.com/api/node', {
  mode: "no-cors",
  method: "GET",
  headers: {
    "Accept": "application/json"
  }
}).then((response) => {
  console.log(response.body); // null
  return dispatch({
    type: "GET_CALL",
    response: response
  });
})
.catch(error => { console.log('request failed', error); });

The problem is that the response body is empty in the .then() function and I'm not sure why. I checked examples online and it looks like my code should work so I'm obviously missing something here. The thing is, if I check the network tab in Chrome's dev tools, the request is made and I receive the data I'm looking for.

Can anybody shine a light on this one?

EDIT:

I tried converting the reponse.

using .text():

fetch('http://example.com/api/node', {
  mode: "no-cors",
  method: "GET",
  headers: {
    "Accept": "application/json"
  }
})
.then(response => response.text())
.then((response) => {
  console.log(response); // returns empty string
  return dispatch({
    type: "GET_CALL",
    response: response
  });
})
.catch(error => { console.log('request failed', error); });

and with .json():

fetch('http://example.com/api/node', {
  mode: "no-cors",
  method: "GET",
  headers: {
    "Accept": "application/json"
  }
})
.then(response => response.json())
.then((response) => {
  console.log(response.body);
  return dispatch({
    type: "GET_CALL",
    response: response.body
  });
})
.catch(error => { console.log('request failed', error); }); // Syntax error: unexpected end of input

Looking in the chrome dev tools:

enter image description here

Mark Fisher
  • 965
  • 1
  • 11
  • 30
Raz
  • 1,601
  • 2
  • 15
  • 23

10 Answers10

233

I just ran into this. As mentioned in this answer, using mode: "no-cors" will give you an opaque response, which doesn't seem to return data in the body.

opaque: Response for “no-cors” request to cross-origin resource. Severely restricted.

In my case I was using Express. After I installed cors for Express and configured it and removed mode: "no-cors", I was returned a promise. The response data will be in the promise, e.g.

fetch('http://example.com/api/node', {
  // mode: 'no-cors',
  method: 'GET',
  headers: {
    Accept: 'application/json',
  },
},
).then(response => {
  if (response.ok) {
    response.json().then(json => {
      console.log(json);
    });
  }
});
Community
  • 1
  • 1
  • 7
    For me all that was required was adding `.then(json => {` onto the end of my `response.json()` statement. Thanks! – Mike Kellogg Apr 23 '18 at 01:22
  • 2
    @MikeKellogg just a heads up that in case you want to keep your code tidy you can `return response.json()` and place the `.then(json=>` part between the last bracket and semicolon. `).then(json=>{});` – Raz Apr 25 '18 at 19:26
  • It seem if you leave off the domain and use a relative URI it works with ``no-cors`` – Danny '365CSI' Engelman Apr 07 '20 at 08:12
  • 3
    I was using AWS API Gateway and in my lambda, I was missing this header ```"Access-Control-Allow-Origin": "*"``` in the response. Adding that and removing ```no-cors``` worked for me. – Kentative Apr 04 '21 at 01:19
  • 3
    For those who were hoping to leave the `no-cors` mode on in an attempt to bypass some CORS restrictions, unfortunately the opaque response that is returned from these types of requests cannot be parsed. I was highly confused, because I could clearly see the correct response in my browsers developer tools, but it was never coming through to my code. More discussion on the topic can be found here: https://stackoverflow.com/a/54906434/3513260 – Fearnbuster Jan 05 '22 at 20:55
40

You will need to convert your response to json before you can access response.body

From the docs

fetch(url)
  .then(response => response.json())
  .then(json => {
    console.log('parsed json', json) // access json.body here
  })
Naisheel Verdhan
  • 4,905
  • 22
  • 34
  • Good answer, and thanks for the reference to the documentation. The docs also show what to do if you're using something other than JSON, such as text - it can display the same behaviors and the solution is very similar (use text() rather than json() ). – Polymeron Jan 03 '22 at 17:44
16

This requires changes to the frontend JS and the headers sent from the backend.

Frontend

Remove "mode":"no-cors" in the fetch options.

fetch(
  "http://example.com/api/docs", 
  {
    // mode: "no-cors",
    method: "GET"
  }
)
  .then(response => response.text())
  .then(data => console.log(data))

Backend

When your server responds to the request, include the CORS headers specifying the origin from where the request is coming. If you don't care about the origin, specify the * wildcard.

The raw response should include a header like this.

Access-Control-Allow-Origin: *
openwonk
  • 14,023
  • 7
  • 43
  • 39
  • This should be the top answer – TotalAMD May 22 '22 at 04:25
  • This worked - and should be the correct answer. In my situation, I had a server running w/ Python Flask and by default, it does not return a CORS header. Initially, I was getting around this with JS by including the "no-cors" and then wondering why the response wasn't being parsed. Changing both frontend and backend did the trick. – Kiran K. Apr 07 '23 at 04:08
7

You must read the response's body:

fetch(url)
  .then(res => res.text()) // Read the body as a string

fetch(url)
  .then(res => res.json()) // Read the body as JSON payload

Once you've read the body you will be able to manipulate it:

fetch('http://example.com/api/node', {
  mode: "no-cors",
  method: "GET",
  headers: {
    "Accept": "application/json"
  }
})
  .then(response => response.json())
  .then(response => {
    return dispatch({
      type: "GET_CALL",
      response: response
    });
  })
Florent
  • 12,310
  • 10
  • 49
  • 58
  • Thanks for the answer! I tried that, but I still can get the correct response. Check the Edited question. – Raz Apr 25 '16 at 12:42
  • @RazvanIlin Use `.json()` instead of `.text()` – Florent Apr 25 '16 at 14:16
  • 1
    Sorry, I just realised I copied the wrong snippet in the last example. I was actually using `json()` there, but I get the Syntax error, which I presume gets fired because it cannot convert the response to json – Raz Apr 25 '16 at 16:02
  • Tried this and it worked for me. `response.json()` did the magic. Once I had it, I was able to console.log and get my response. Thanks – Hanmaslah Jan 11 '17 at 09:29
  • 3
    Still trying to wrap my head around this `no-cors` mode. I'm in a similar situation. Using variations of the solutions here the no-cors fetch seems to succeed... no x-origin error, and in my network inspector I can SEE data in Preview and Response. But I just can't seem to access the data from the response object passed into my `then`. `json() text()` neither seems to do much. Does an opaque response mean it gets the data then `null`s it so it can't be accessed after completed? :-/ – jmk2142 Apr 14 '17 at 01:55
4
fetch("http://localhost:8988/api", {
        //mode: "no-cors",
        method: "GET",
        headers: {
            "Accept": "application/json"
        }
    })
    .then(response => {
        return response.json();
    })
    .then(data => {
        return data;
    })
    .catch(error => {
        return error;
    });

This works for me.

jotasi
  • 5,077
  • 2
  • 29
  • 51
3
fetch("http://localhost:8988/api", {
    method: "GET",
    headers: {
       "Content-Type": "application/json"
    }
})
.then((response) =>response.json());
.then((data) => {
    console.log(data);
})
.catch(error => {
    return error;
});
  • 2
    While this code may answer the OP's question, a few words of explanation would go a long way to help current and future readers understand your response. – Thom Aug 24 '18 at 14:48
  • This is actually a very similar example as other answers here. Not sure about SO's policies, but I'm not sure this is allowed – Raz Aug 24 '18 at 20:36
  • Only it's not the same. "Content-Type" is unique. Too bad it didn't work for me though :( – Michael Jan 06 '19 at 16:52
1

Try to use response.json():

fetch('http://example.com/api/node', {
  mode: "no-cors",
  method: "GET",
  headers: {
    "Accept": "application/json"
  }
}).then((response) => {
  console.log(response.json()); // null
  return dispatch({
    type: "GET_CALL",
    response: response.json()
  });
})
.catch(error => { console.log('request failed', error); });
Damien Leroux
  • 11,177
  • 7
  • 43
  • 57
0

In many case you will need to add the bodyParser module in your express node app. Then in your app.use part below app.use(express.static('www')); add these 2 lines app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json());

Brianhenry
  • 195
  • 1
  • 8
0

in my case, it was not cors. For some reason, the client got an empty response body if fetch was invoked from the child component, as soon as I moved the data fetching to the root App component the client got the proper response body. I don't know why, just went with it. If someone could explain it, that would be great.

Victor Di
  • 988
  • 10
  • 16
0

My problem was that I was setting the response status code to 204 in my PHP backend. Guess it makes sense since 204 means 'No Content'.

John Muraguri
  • 426
  • 7
  • 6