1

I'm trying to pipe a file from service A trough service B into my Postman cliente. Service A builds an delivers a CSV file, and service B (nodejs) has to pipe into my client.

After researching a lot I have managed to successfully pipe the files into service B and then into Postman. Now I want to handle the ugly cases: what if the request token is invalid? What if I can't find the file?

As of this moment, I have found zero documentation or examples on how successfully handle errors while piping a request using superagent.

This is what I have so far

router.post("/csv", (req, res) => {
  download_csv(req.get("Authorization"), req.body.ids)
  .then((response) => {
    res.sendFile(path.resolve(response));
  })
  .catch((err) => {
    res.status(error.status).json(error.response.body);
  })   
});

function download_csv(token, ids) {
  const stream = fs.createWriteStream("filters.csv")
  let request = agent
    .post(`${profiles}/api/documents/csv`)
    .set("authorization", token)
    .send({
      ids: ids,
      action: DOWNLOAD_CSV_PROFILES
    })

  request.on("response", res => {
    // Maybe I can use abort to handle this thing, but can't figure out how!
    // if (res.status !== 200) request.abort()
    console.log(res.status)
  })

  request.on("abort", () => {
    console.log("aborted")
    return new Promise((resolve, reject) => {
      resolve("request aborted")
    })
  })

  request.pipe(stream)
  return streamToPromise(stream);
}

function streamToPromise(stream) {
  return new Promise((resolve, reject) => {
    stream.on("error", (err) => {
      console.log("error in error")
    })
    stream.on("finish", () => {
      console.log("File saved")
      resolve(stream.path);
    });
  });
}

This code handles the creation of the files correctly. When I fake the token or misspell the Authorization header, I get a correct 401 response, but a file gets written anyway with its contents being the authentication error.

Can anyway give me a hint on how to:

  • actually catch and manage the request when fails
  • in such case, how to escape the piping by going back to the express context and just returning a failed express request?

Many thanks!

Sebastialonso
  • 1,437
  • 18
  • 34

1 Answers1

2

If I understand you correctly, simply create the fs write stream in on('response') and make a small fix on the resultion.

function download_csv(token, ids) {
    return new Promise((resolve, reject) => {
      let request = agent
        .post(`${profiles}/api/documents/csv`)
        .set("authorization", token)
        .send({
          ids: ids,
          action: DOWNLOAD_CSV_PROFILES
        })

      request.on("response", res => {
        // Maybe I can use abort to handle this thing, but can't figure out how!
        if (res.status === 200) {
            res
              .on("end", resolve)
              .pipe(fs.createWriteStream("filters.csv"));
        } else {
          reject();
        }
      })

      request.on("abort", reject);

  });
}

I'm not sure what is the "request" you're using - but assuming it's actually the request npm module that will help.

Ideally, upload the file to a temporary directory and move it when the promise is resolved, delete on rejected. This way you'll solve the issue of partial downloads.

If you want to make any on-the-fly transforms, check out my "scramjet". It'll make everything easier with promises.

Michał Karpacki
  • 2,588
  • 21
  • 34
  • request comes from the superagent library. Thank youfor your suggestion, I will try it! – Sebastialonso Jan 31 '18 at 23:35
  • After trying this out, I don't think it can catch socket hangup errors (ECONNRESET). – Andy Dec 11 '19 at 01:09
  • @Andy you should be able to get to the underlying socket somehow, besides if the lib is written sensibly there should also be `request.on("error")` event to watch on... – Michał Karpacki Dec 11 '19 at 09:31
  • I tried using `request.on('error')` but it didn't seem to work. I was encountering really confusing behavior where Node was crashing without any error messages, even when I added uncaught error/rejection handlers...I only saw the ECONNRESET when using request.then without streams on the same URL. – Andy Dec 12 '19 at 08:21
  • How about `res.on('error', reject)` in `request.on("response", ...)`? – Michał Karpacki Dec 12 '19 at 09:37