Skip to main content

Streaming basics

The Response object supports streaming via the standard body property, which returns a ReadableStream<Uint8Array>. This allows you to process large responses incrementally.
import { fetch } from 'wreq-js';

const response = await fetch('https://example.com/large-file', {
  browser: 'chrome_142',
});

const stream = response.body;
if (stream) {
  const reader = stream.getReader();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    console.log(`Received ${value.byteLength} bytes`);
  }
}

Key behaviors

The body property is lazily initialized. It returns null for empty responses or a ReadableStream for non-empty responses.
Accessing response.body does not mark it as consumed. Only reading from the stream sets bodyUsed to true.
You can call response.clone() to create a duplicate response before the body is consumed.
Once consumed via json(), text(), arrayBuffer(), or stream reading, the body cannot be read again.

Processing chunks

Process data as it arrives:
const response = await fetch('https://example.com/stream', {
  browser: 'chrome_142',
});

const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');

const decoder = new TextDecoder();
let result = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  // Decode chunk and process
  const text = decoder.decode(value, { stream: true });
  result += text;
  
  // Process incrementally
  console.log('Chunk:', text.substring(0, 100));
}

Download with progress

Track download progress:
const response = await fetch('https://example.com/file.zip', {
  browser: 'chrome_142',
});

const contentLength = response.headers.get('content-length');
const total = contentLength ? parseInt(contentLength, 10) : 0;

const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');

let received = 0;
const chunks: Uint8Array[] = [];

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  chunks.push(value);
  received += value.byteLength;

  if (total) {
    const progress = ((received / total) * 100).toFixed(1);
    console.log(`Progress: ${progress}%`);
  }
}

// Combine chunks into single array
const allChunks = new Uint8Array(received);
let position = 0;
for (const chunk of chunks) {
  allChunks.set(chunk, position);
  position += chunk.byteLength;
}

Server-Sent Events (SSE)

Parse Server-Sent Events from a stream:
const response = await fetch('https://example.com/events', {
  browser: 'chrome_142',
  headers: { Accept: 'text/event-stream' },
});

const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');

const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  
  // Process complete events
  const events = buffer.split('\n\n');
  buffer = events.pop() || ''; // Keep incomplete event in buffer
  
  for (const event of events) {
    if (event.startsWith('data: ')) {
      const data = event.slice(6);
      console.log('Event:', JSON.parse(data));
    }
  }
}