1

I am having an API route setup in my Next.js project. The API route calls 3rd party API using fetch call. The API call returns a string which contains an XML data.

I am trying to serve this string as downloadable file (like test.xml) inside browser, but I can't make it work. This is how far I have come, but browser would not trigger file download.

My /pages/api/registration.js:

import stream from 'stream';
import { promisify } from 'util';
import fetch from 'node-fetch';

const pipeline = promisify(stream.pipeline);
const url = process.env.API_URL + 'registration';

const handler = async (req, res) => {
  const headers = new Headers();
  headers.append('Accept', 'application/json');
  headers.append('Content-Type', 'application/json');

  const response = await fetch(url, {
    method: 'POST',
    mode: 'same-origin',
    credentials: 'include',
    redirect: 'follow',
    headers: headers,
    body: JSON.stringify(req.body),
  });

  if (!response.ok) throw new Error(`unexpected response ${response.statusText}`);
  
  res.setHeader('Content-Type', 'text/xml');
  res.setHeader('Content-Disposition', 'attachment; filename=test.xml');
  await pipeline(response.body, res);
}

export default handler;

Any ideas?

Primoz Rome
  • 10,379
  • 17
  • 76
  • 108
  • Is it showing the file (response) properly if you test that endpoint using postman/insomnia? – brc-dd Dec 05 '22 at 13:53
  • I tested you exact code, just replacing your url `const url = "https://example.com/";` it downloaded the file just fine, what error your getting? – Paulo Fernando Dec 05 '22 at 14:11
  • @PauloFernando I am not getting any error. The NextJS api route returns 200 and in Chrome inspector it outputs the XML string in the response... but file download is not triggered. – Primoz Rome Dec 05 '22 at 14:51
  • @brc-dd API returns 200 with this response `{"xml":"\n\n \n \n \n \n <\/Packages>\n <\/License>\n<\/LicenseFile>\n","already_registered":true}`. No download is triggered. – Primoz Rome Dec 05 '22 at 15:01
  • Maybe try `const { xml } = await response.json(); await pipeline(xml, res);` instead? Also are you calling this API route using JS or a simple anchor tag? If you're calling this using JS then you need to handle the file downloading yourself: https://stackoverflow.com/a/32318930/11613622 – brc-dd Dec 05 '22 at 15:06
  • @brc-dd Yeah tried that right now. Still no download is triggered by the browser. Here is the video: https://www.icloud.com/iclouddrive/0fcKkAdoAIsxwZcLwWysbLONQ#screen-recording – Primoz Rome Dec 05 '22 at 15:23
  • @PrimozRome One can't access that iCloud link unless you explicitly add them. Can you share it using some open platform? Here is a demo of that solution working just fine: https://stackblitz.com/edit/nextjs-v5hebf?file=pages%2Findex.js,pages%2Fapi%2Fhello.js Also, it might be an issue with just Safari. Can you try opening it on some other browser? – brc-dd Dec 05 '22 at 15:53
  • @brc-dd here is the Dropbox linke to screenrecording https://www.dropbox.com/s/2gmihohpj149ylf/screen-recording.mov?dl=0. I am working on Google Chrome browser. I get the same result on Chrome and Safari... – Primoz Rome Dec 05 '22 at 16:13
  • @brc-dd hey your demo on stackblitz works for me too. I don't really get it as we have identical code. But I see you are manipulating "Download" button and attributes... maybe that is the case, as I am not doing anything like that. – Primoz Rome Dec 05 '22 at 16:18
  • 1
    @brc-dd my problem was not in the API route but in the component where I am calling the API route. I have added this part and now it works -> `el.setAttribute('href', 'data:text/xml;charset=utf-8,' + encodeURIComponent(xml));` and everything related to that... Thanks a lot! – Primoz Rome Dec 05 '22 at 16:32

0 Answers0