Search code examples
streamfetch-apiwhatwg-streams-api

Fetch with ReadableStream as Request Body


I'm trying to use fetch with a ReadableStream. In this example, the ReadableStream should simply repeat "Some data..." indefinitely.

fetch('/', {
  method: 'POST', 
  body: new ReadableStream({
    pull: function(controller) {
      console.log('pull called!');
      controller.enqueue('Some data...');
    }
  })
});

This doesn't work. While pull is executed once, no data is sent in the request body.

POST / HTTP/1.1
Host: example.com
Connection: keep-alive
Content-Length: 0
Origin: https://example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Accept: */*
Referer: https://example.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

How can I make a ReadableStream (or any kind of stream where I can write dynamic data) usable with fetch?

Alternatively, if this isn't yet possible, could you please indicate this? Thank you.

Note: This is a more specific spin-off question from: Method for streaming data from browser to server via HTTP


Solution

  • Update in 2024: As documented on MDN, using request streams is possible in all important browsers except Firefox since September 2022. The implementation in Firefox is tracked in this issue.

    It should be noted that even though most browser support request streams now, there are some restrictions, as discussed in this issue:

    • To use a ReadableStream as the body, you also must specify duplex: "half" according to the latest specification. Some browsers might not enforce this yet. This setting means that you will only start receiving the response stream after the request stream has finished. In the future, support for duplex: "full" may be added, which would enable starting to receive the response while the request is still sending.
    • Right now, browsers only support request streams over HTTP/2 and HTTP/3, but not HTTP/1. Since browsers only permit HTTP/2 and HTTP/3 over HTTPS, request streaming is not possible right now over unencrypted connections.

    As a workaround, you could change your backend API to support uploading chunks of the data in separate requests, or you could use a websocket to transmit the data. If you don’t have control over the backend, you could set up an intermediate backend that forwards the chunks as a request stream.