BlogJavaScript

How to get response body from fetch() in javascript

Written by Codemzy on April 11th, 2024

When you use the JavaScript Fetch API, `response.body` might not work like you hoped it would. So if you run into errors like "Cannot read properties of undefined (reading 'body')", this blog post will help you fix it!

If you're short on time you can scroll to the bottom for the working code!

If you use JavaScript to make requests or fetch responses across the network, you can use .fetch(). And when working with .fetch(), you might need to read the body of a request or a response.

After all, that's where all the juicy data is!

I thought I'd be able to read the response body like this:

async function myFetchFunction() {
  const response = await fetch("http://api.example.com/user/12345");
  console.log(response.body.name); // undefined
}

But no! I got that dreaded undefined. Or worse, an error "Cannot read properties of undefined (reading 'body')".

But that shouldn't be the case. I know the response sends { id: 12345, name: "Codemzy" } back. So where is that body going?

Now, I usually use Axios as my JavaScript HTTP client, and I use Express in Node.js to accept HTTP requests, so I have to admit I may be a little late to the party on .fetch().

However, I recently created a function in Cloudflare to collect CSP violation reports, which needed to collect POST requests. And incoming requests to Cloudflare workers have a Request object as the request property.

The RequestOpen external link interface represents an HTTP request and is part of the Fetch API.

- Cloudflare Request

And .fetch() is natively available inside Cloudflare workers so I wanted to avoid installing external libraries and use .fetch() to send requests in my function too.

But I ran into a few problems, and late last night I realised - I needed to learn .fetch()!!

Here's what I discovered...

Reading from the response.body doesn't work

In Axios, I would get the response body with response.data.

axios.get('/user/12345')
  .then(function (response) {
    console.log(response.data.name); // "Codemzy"
  });

In Express.js I can get the request body with request.body.

And this is when I discovered that response.body doesn't work in .fetch().

But when I read the Fetch API docs, I was kinda confused, because it looks like there's a response.body, but when I tried to get the data it was undefined!

async function myFetchFunction() {
  const response = await fetch("http://api.example.com/user/12345");
  console.log(response.body.name); // undefined
}

After some further digging (or in this case console.log()-ing, my favourite type of bug fixing!), and reading the docs with a little more attention, I discovered that response.body is a readable stream.

async function myFetchFunction() {
  const response = await fetch("http://api.example.com/user/12345");
  console.log(response.body); // ReadableStream
}

And what that means for you and me, is that we can't read the data from it. Not yet, anyway.

Convert the response with .json()

To read the response body, we need to convert it from a readable stream.

In this case, I need a javascript object, since the response I'm expecting is a JSON* object, and I want to parse it from JSON to a JavaScript object. So I'll use the .json() method available in the response.

The json() method of the Response interface takes a Response stream and reads it to completion. It returns a promise which resolves with the result of parsing the body text as JSON.

- MDN Response: json() method

And while we are parsing the body, we are not touching the body. As in, we don't call .json() on the body.

This won't work:

let data = response.body.json();

We actually need to call the .json() method on the response object to get the body.

let data = response.json();

This bit confuzzled me a little.

I thought, since we are parsing the response, everything would now be parsed in this data object - e.g. data.headers, data.body, etc. But it's not. Headers are still on the original response object (response.headers) - it's just the body in data (now as an object instead of a readable stream).

So now data is the body. You could even call it body if you want.

let body = response.json();

Just don't get it mixed up with response.body - the readable stream!

*If your response isn't JSON, you can use other methods to read the response including .blob(), .formData() and .text().

Final code

async function myFetchFunction() {
  const response = await fetch("http://api.example.com/user/12345");
  let body = response.json(); // parse the response body
  console.log(body.name); // "Codemzy"
}