Home > Software engineering >  How to consume streamed response from res.write() in front-end using Fetch
How to consume streamed response from res.write() in front-end using Fetch

Time:10-03

I have an ExpressJs endpoint returning chunks of data using res.write() like this:

process.stdout.on("data", (data) => {
    const progress = getDownloadProgress(data.toString());
    res.write(JSON.stringify(progress));
  });

process.stdout.on("end", (data) => {
    res.end();
  });

Then at my front-end side, I struggle to consume those chunks of progress values like 10%, 66%. Here is what I have so far used ReadableStream with the Fetch API:

fetch("http://localhost:4000/download")
    .then((response) => response.body)
    .then((rb) => {
      const reader = rb.getReader();

      return new ReadableStream({
        start(controller) {
          // The following function handles each data chunk
          function push() {
            // "done" is a Boolean and value a "Uint8Array"
            reader.read().then(({ done, value }) => {
              // If there is no more data to read
              if (done) {
                console.log("done", done);
                controller.close();
                return;
              }
              // Get the data and send it to the browser via the controller
              controller.enqueue(value);
              // Check chunks by logging to the console
              console.log(done, value); // I'm seeing what I assume to be a stream of my chunks here, but its in array buffer format
              push();
            });
          }

          push();
        },
      });
    })
    .then((stream) => {
      // Respond with our stream
      return new Response(stream, {
        headers: { "Content-Type": "application/json" },
      }).json();
    })
    .then((result) => {
      // Do things with result
      console.log(result);
    });

It's just a snippet from MDN.

From the console.log, I'm getting a stream of what I assume to be my chunks. But they are in array buffer format - not something I can use to update my UI.

I guess my basic question is: How to have my front-end consume the streamed response sent through res.write() from my ExpressJs?

CodePudding user response:

According to the response.write() method documentation, it takes three parameters:

response.write(chunk[, encoding][, callback]);

So, try to add encoding(which is usually utf-8) in your write method:

process.stdout.on("data", (data) => {
    const progress = getDownloadProgress(data.toString());
    res.write(JSON.stringify(progress), "utf-8");
  });

CodePudding user response:

I decided to just decode the array buffer using TextDecoder:

const decoder = new TextDecoder();

const response = await fetch("http://localhost:4000/download");
  const reader = response.body.getReader();

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

    try {
      // decodedChunk will have your literal values
      const decodedChunk = JSON.parse(decoder.decode(value));
    } catch (e) {}
  }
  • Related