Search code examples
javascriptnode.jsblobnext.js13nextjs-api-router

How to properly send a blob or file as api response in next 13


I am trying to send a blob (will be a file from another API in future) as the response for a api call in next13 api routes. My handler goes like:

export default async function handler(req, res) {
  const endpoint = `${process.env.UI_ENGINE}/docs/${req.query.id}/download-doc`;

  
    const response = await fetch(endpoint, {
       headers: { Authorization: `${req.headers.authorization}` },
    });

    const blob = await response.blob();
 

    console.log("»»»»»» [DEBUG : blob ] -- :", blob); // Blob { size: 1545, type: 'application/json' }

    res.status(response.status).send(blob);
}

I am calling this api from my component like:

const fetchWithToken = async (service, token) => {
// data fetch function in UI.
    const response = await fetch(`${process.env.basePath}/${service}`, {
      headers: { Authorization: `Bearer ${token}` },
    });

    const blob = await response.blob();

    console.log("»»»»»» [DEBUG : response ] --  :", blob); // Blob {size: 2, type: 'application/json'}

// download as file logic ....
}

as you can see the blob in ui data fetch response gives a blob size of 2 (content will be an empty object) even if the expected size is bigger.

I tried to convert the blob to a readable stream and send it back to the ui from next api. also tried to make use of File(). both idea failed. I was able to convert the blob to text in api (blob.text()) and send it as the response; but it will work only for json,csv,etc... files. But the scope is wide.

I am using nextjs - 13.5.4 node - 18.17.1.

I would prefer to send the file from UI_ENGINE to frontend directly. Please help me to find the best solution. thanks in advance...


Solution

  • In the Next.js API handler function, after reading the response blob from the respective API, I attempted to convert it into a stream, array buffer, and file to send the response back to the reading function properly. However, none of these methods worked well. Since I couldn't pinpoint the exact issue, I tried converting the blob to raw buffer and sending it as a response, which surprisingly worked.

    To do this first convert the blob to and array buffer - const arrayBuffer = await blob.arrayBuffer(); then use the arraybuffer to create new raw buffer - const buffer = Buffer.from(arrayBuffer);

    Since I am sending a file it is important to include some key headers along with the response

    export default async function handler(req, res) {
      const endpoint = `${process.env.UI_ENGINE}/docs/${req.query.id}/download-doc`;
    
      try {
        const response = await fetch(endpoint, {
          headers: { Authorization: `${req.headers.authorization}` },
        });
    
        const blob = await response.blob();
    
        const contentType =
          response.headers.get("content-type") || "application/octet-stream";
        const contentLength = response.headers.get("content-length") || blob.size;
    
        // converting blob to raw buffer for transferring
        // (other formats including blob was not working)
        const arrayBuffer = await blob.arrayBuffer();
        const buffer = Buffer.from(arrayBuffer);
    
        res
          .status(200)
          .setHeader("Content-Type", contentType)
          .setHeader("Content-Length", contentLength)
          .send(buffer);
      } catch (error) {
        res.status(500).send({ message: "Error fetching the document", error });
      }
    }
    

    I am not expecting this to be the exact solution. There has to be a better way to do this.