Skip to main content
Fetch API - Body Data

Fetch API - Body Data

Fetch exposes the message body as a stream and offers convenience methods to consume it in the shape you need. Each response body can be read exactly once.

Body methods

  • text() - the body as a UTF-8 string.
  • json() - parses the body as JSON.
  • blob() - a binary object with a MIME type, useful for downloads.
  • arrayBuffer() - raw bytes as an ArrayBuffer.
  • formData() - parses multipart or URL-encoded form data.
const response = await fetch('/api/profile');

const data = await response.json();
const text = await response.text(); // throws: body already used

Parsing JSON safely

async function readJson(response) {
  const text = await response.text();

  if (!text) {
    return null;
  }

  try {
    return JSON.parse(text);
  } catch (error) {
    throw new Error('Server returned invalid JSON');
  }
}

Reading text first gives you a clear error path when a proxy returns an HTML error page with a JSON content type.

Request bodies

The same body types can be sent: a string, Blob, ArrayBuffer, typed array, FormData, URLSearchParams, or a ReadableStream.

// JSON
await fetch('/api/notes', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: 'Draft' })
});

// Form data with a file
const form = new FormData();
form.append('title', 'Draft');
form.append('attachment', fileInput.files[0]);

await fetch('/api/notes', { method: 'POST', body: form });

// URL-encoded
await fetch('/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({ user: 'ana', remember: '1' })
});

When you send FormData, do not set Content-Type yourself; the browser adds the multipart boundary.

Reading chunks from a stream

const response = await fetch('/api/logs/live');
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  appendOutput(decoder.decode(value, { stream: true }));
}

Practical notes

  • Check bodyUsed before attempting a second read.
  • Use clone() if both a parser and a logger need the body.
  • Large binary bodies should be stored as blobs rather than converted to text.
  • Always release readers when finished so the connection can be freed.