268

How may I get information from a ReadableStream object?

I am using the Fetch API and I don't see this to be clear from the documentation.

The body is being returned as a ReadableStream and I would simply like to access a property within this stream. Under Response in the browser dev tools, I appear to have this information organised into properties, in the form of a JavaScript object.

fetch('http://192.168.5.6:2000/api/car', obj)
    .then((res) => {
        if(res.status == 200) {
            console.log("Success :" + res.statusText);   //works just fine
        }
        else if(res.status == 400) {
            console.log(JSON.stringify(res.body.json());  //res.body is undefined.
        }

        return res.json();
    })
VLAZ
  • 22,934
  • 9
  • 44
  • 60
noob
  • 4,744
  • 5
  • 17
  • 31
  • 3
    @FrancescoPezzella Thanks for the response. I have tried `response.Body.json()` , but I am getting _italic_ TypeError: Cannot read property 'json' of undefined _italic_ . Is this because the bodyUsed property is also set to false? However I can view this body under the response tab in browser developer tools. There is an error message which I'd like to retrieve. – noob Nov 03 '16 at 10:35
  • So your issue is purely related to the error 400 condition? What happens if you change the handler to `console.log(res.json());`? Do you see the data you are expecting? – Ashley 'CptLemming' Wilson Nov 03 '16 at 16:32
  • @noob Are you trying to read the response as a stream if `res.status == 200`? – guest271314 Dec 18 '16 at 20:11
  • Is it just me or that documentation is *plain* wrong? I did fix it with the solutions on this answers though. – Lucio May 25 '18 at 19:15
  • I know it has been a while but for the sake of keeping stackoverflow great, please just accept the right answer. The one with over 200 upvotes. – xaddict Dec 24 '20 at 12:57

8 Answers8

336

In order to access the data from a ReadableStream you need to call one of the conversion methods (docs available here).

As an example:

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(function(response) {
    // The response is a Response instance.
    // You parse the data into a useable format using `.json()`
    return response.json();
  }).then(function(data) {
    // `data` is the parsed version of the JSON returned from the above endpoint.
    console.log(data);  // { "userId": 1, "id": 1, "title": "...", "body": "..." }
  });

EDIT: If your data return type is not JSON or you don't want JSON then use text()

As an example:

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(function(response) {
    return response.text();
  }).then(function(data) {
    console.log(data); // this will be a string
  });

Hope this helps clear things up.

  • 1
    Thanks for the response. I have tried this and am still getting the same error where res.body is undefined. I am able to retrieve the status however in first then() function with res.status. It seems that only the body is a ReadableStream object. It does seem to have a property locked, which is set to true? – noob Nov 03 '16 at 14:41
  • Where are you trying to access `res.body` (this isn't part of my example)? Can you share some sample code in your original question to make it clearer where your problem might be. – Ashley 'CptLemming' Wilson Nov 03 '16 at 15:28
  • 1
    I tried accessing res.body from the json response that was returned in first .then() function. I have added a sample to my original question for more clarity. Thanks! – noob Nov 03 '16 at 16:28
  • 1
    Awesome, using react and request native, and wondering what in the world to do with a ReadableStream, and this did the trick. ++ – edencorbin Feb 12 '17 at 01:54
  • 1
    Just a headsup, seems like a no-brainer, but make sure the backend you're hitting is actually providing valid JSON! Definitely not speaking from experience. – abelito Apr 18 '19 at 22:24
  • What if the response of the ReadableStream is an image? – Azurespot Nov 09 '19 at 04:25
  • @Azurespot It depends on what you want to do with the Image. You could call one of these methods https://developer.mozilla.org/en-US/docs/Web/API/Body, for example `const imageBlob = await response.blob();` and then `const objectUrl = URL.createObjectURL(imageBlob); htmlImageElement.src = objectUrl;`. The returned URL is released automatically when the document is unloaded. But if your page has dynamic use, you should release it explicitly by calling window.URL.revokeObjectURL(objectUrl). Or use a FileReader. – Stefan Rein Jan 15 '20 at 13:30
  • This helped me so much! Thank you! – lumayara Jan 15 '21 at 17:44
  • thank you so so so much for this resposne! I was bashing my face against everything not understanding all this "readable stream" api documentation and its all about chunks & text files and crap im not using. this worked! I haven't done backend work in over a year so i'm so rusty, this looks like what I used to learn/write and I knew I was forgetting something like this - this was it! – PhilosophOtter Feb 08 '21 at 20:47
  • 1
    Link is broken, try this: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream – Alex W Sep 23 '21 at 15:20
78

Some people may find an async example useful:

var response = await fetch("https://httpbin.org/ip");
var body = await response.json(); // .json() is asynchronous and therefore must be awaited

json() converts the response's body from a ReadableStream to a json object.

The await statements must be wrapped in an async function, however you can run await statements directly in the console of Chrome (as of version 62).

Noel
  • 2,853
  • 21
  • 40
37

res.json() returns a promise. Try ...

res.json().then(body => console.log(body));
pinoyyid
  • 20,293
  • 12
  • 57
  • 108
19

Little bit late to the party but had some problems with getting something useful out from a ReadableStream produced from a Odata $batch request using the Sharepoint Framework.

Had similar issues as OP, but the solution in my case was to use a different conversion method than .json(). In my case .text() worked like a charm. Some fiddling was however necessary to get some useful JSON from the textfile.

Bob Kaufman
  • 12,534
  • 16
  • 80
  • 104
Dan Mehlqvist
  • 192
  • 1
  • 5
  • 2
    Thank you! This worked for me. I am sending an Illuminate http response from my Laravel server with a simple `return $data;`. I was finally able to read this response in the browser with `fetch(...).then(response => response.text()).then(data => console.log(data));` – Cameron Hudson Jul 25 '18 at 15:17
13

Note that you can only read a stream once, so in some cases, you may need to clone the response in order to repeatedly read it:

fetch('example.json')
  .then(res=>res.clone().json())
  .then( json => console.log(json))

fetch('url_that_returns_text')
  .then(res=>res.clone().text())
  .then( text => console.log(text))
Chris Halcrow
  • 25,566
  • 14
  • 148
  • 174
11

If you just want the response as text and don't want to convert it into JSON, use https://developer.mozilla.org/en-US/docs/Web/API/Body/text and then then it to get the actual result of the promise:

fetch('city-market.md')
  .then(function(response) {
    response.text().then((s) => console.log(s));
  });

or

fetch('city-market.md')
  .then(function(response) {
    return response.text();
  })
  .then(function(myText) {
    console.log(myText);
  });
Alexis Tyler
  • 1,353
  • 6
  • 30
  • 47
AlexChaffee
  • 7,822
  • 2
  • 48
  • 55
4

I dislike the chaining thens. The second then does not have access to status. As stated before 'response.json()' returns a promise. Returning the then result of 'response.json()' in a acts similar to a second then. It has the added bonus of being in scope of the response.

return fetch(url, params).then(response => {
    return response.json().then(body => {
        if (response.status === 200) {
            return body
        } else {
            throw body
        }
    })
})
Mardok
  • 1,210
  • 10
  • 14
  • The chaining `then` helps you retrieve the final resolved value (the `body`). Nesting them prevents you from being able to get the `body` value without a callback or some mechanism of the sort. Imagine this: `let body = await fetch(...).then(res => res.json()).then(data => data)`. This wouldn't work in the nested way. To check for `response.status` you can always `throw` an exception inside the first `then`, and add a `catch` to the whole promise chain. – Óscar Gómez Alcañiz May 28 '19 at 11:27
  • 1
    AGREE. Preferred in enterprise environment. : ) – Nash Worth Nov 10 '20 at 20:28
-1

I just had the same problem for over 12 hours before reading next, just in case this helps anyone. When using nextjs inside your _api page you will need to use JSON.stringify(whole-response) and then send it back to your page using res.send(JSON.stringify(whole-response)) and when it's received on the client side you need to translate it back into json format so that it's usable. This can be kinda figured out by reading their serialization section. Hope it helps.