javascriptnode.jsstreamingfetch-apifastify

Return fetch stream from Fastify


My Fastify server needs to proxy a request to another service, and when I get the response I am trying to just pass the stream back down to the client, but the response is empty. Here's an example I created using a popular open API; Star Wars API.

const fastify = require("fastify")({ logger: true });

// return JSON from SWAPI - works!
fastify.get("/api/json", async (request, reply) => {
  const response = await fetch("https://swapi.dev/api/people/1/");
  const json = await response.json();
  return reply.send(json);
});

// return stream from SWAPI - doesn't work
// response is an empty object: {}
fastify.get("/api/stream", async (request, reply) => {
  const response = await fetch("https://swapi.dev/api/people/1/");
  return reply.send(response.body);
});

fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
});

Above is the entire server file, but you can also clone my GitHub repo if you want to: https://github.com/TJBlackman/fastify-stream-test


Solution

  • A Fastify contributor actually answered this question on Github:

    'use strict'
    import { Readable } from "node:stream";
    
    const fastify = require(".")({ logger: true });
    
    fastify.get("/api/stream", async (request, reply) => {
      const response = await fetch("https://swapi.dev/api/people/1/");
      const stream = Readable.fromWeb(response.body);
      return reply.send(stream);
    });
    
    fastify.listen({ port: 3000 }, (err) => {
      if (err) {
        fastify.log.error(err);
        process.exit(1);
      }
    });
    

    I guess, fetch returns a WHATWG Stream, which is not providing a pipe method, thus not being streamed when using reply.send.

    The Fastify team merged a PR to fix this, and now it just works, as long as you're on Fastify v4.26.0 or higher!

    fastify.get("/api/stream", async (request, reply) => {
      const response = await fetch("https://swapi.dev/api/people/1/");
      return reply.send(response.body);
    });