How to POST with a body as readable stream?

I’m tying to upload a file as a readable stream. Here’s my client code:

    const stream = new ReadableStream({
      async start(controller) {
        console.log("stream start");
      },
      async pull(controller) {
        controller.enqueue("hello before a delay");
        await wait(1000);
        controller.enqueue("hello after a delay");
        controller.close();
      },
      cancel() {
      }
    });

    let res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'text/plain' },
      body: stream,
    });

And here is my code on the backend:

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
  let readable_stream = await request.text();
  const res = new Response('ok');
  res.headers.set('access-control-allow-origin', '*');
   ...// some other headers
  return res;
}

I’m trying to read the stream on the server side, but I can’t. Awaiting request.text() returns a [object ReadableStream]. Then that object doesn’t have a getReader() method?

Uncaught (in promise)
TypeError: o.getReader is not a function
    at Object.e.exports [as handler] (worker.js:1:3227)
    at async e.exports (worker.js:1:1131)
Uncaught (in response)
TypeError: o.getReader is not a function

I’m assuming the client in this case is a browser, and the server is a worker. Using a ReadableStream as a request body is actually a relatively new addition to browsers. Chrome 85 has experimental support for it hidden behind a flag (see https://web.dev/fetch-upload-streaming/); I’m not sure about other major browsers.

The Request constructor defaults to stringifying the body object being passed in if it doesn’t recognize the object’s type. The stringification of a ReadableStream object is [object ReadableStream], which is why your worker is seeing that string in the body.

You can read more about the feature’s long history in the Fetch spec: https://github.com/whatwg/fetch/issues/88

1 Like

So the request constructor doesn’t recognize a ReadableStream object? Is there any other way to get progress on a large POST request?

That’s correct, the Request constructor as implemented in most major browsers does not recognize ReadableStream as a body, though I suspect the feature will land at some point in the not so distant future.

I’m actually not very experienced in browser-side JavaScript, so I’m not sure what other options there are for monitoring upload progress. Maybe someone else has ideas.