What is the Axios equivalent of req.pipe(request()) / Pipe express request data into Axios request

Viewed 1173

Using request you can forward a POST multipart/form-data request from express to another server without modifying the body/parameters of the initial request and then return the response from the other server to express. With axios this feature appears to be missing.

CODE: header

const request = require('request');
const axios = require('axios');

const express = require('express');
const app = express();
app.listen(3000);

const FORWARD_URL = 'https://example.com/'

Working example using request

app.post('/test/0', (req, res) => {
    req.pipe(request(FORWARD_URL)).pipe(res);
})

Attempt #1

app.post('/test/1', (req, res) => {
    req.pipe(axios.post(FORWARD_URL)).pipe(res);
})

// internal/streams/readable.js:827
//       dests[i].emit('unpipe', this, { hasUnpiped: false });
//                ^
// TypeError: dests[i].emit is not a function
//     at IncomingMessage.Readable.unpipe (internal/streams/readable.js:827:16)
//     at unpipe (S:\_Work\[REDACTED]\node_modules\unpipe\index.js:47:12)
//     at send (S:\_Work\[REDACTED]\node_modules\finalhandler\index.js:306:3)
//     at Immediate.<anonymous> (S:\_Work\[REDACTED]\node_modules\finalhandler\index.js:133:5)
//     at Immediate.<anonymous> (S:\_Work\[REDACTED]\node_modules\express\lib\router\index.js:635:15)
//     at processImmediate (internal/timers.js:466:21)

Attempt #2

app.post('/test/2', (req, res) => {
    req.pipe(axios({
        url: FORWARD_URL,
        method: 'POST',
        responseType: 'stream'
    })).pipe(res);
})

// SAME ERROR AS ABOVE

Attempt #3

app.post('/test/3', async (req, res) => {
    const axiosRequest = await axios({
        url: FORWARD_URL,
        method: 'POST',
        responseType: 'stream',
        data: req
    })

    axiosRequest.data.pipe(res);
})
// server at FORWARD_URL receives improperly formatted request body, changing request content-type headers has no affect
// ------WebKitFormBoundaryc4BjPwpdR4mG7CFN
// Content-Disposition: form-data; name="field_name"
//
// field_value
// ------WebKitFormBoundaryc4BjPwpdR4mG7CFN--

A similar issue has been addressed here, the accepted answer, while not very clear, does answer the question, however, only covers GET & POST application/x-www-form-urlencoded requests, This is regarding POST multipart/form-data requests.

Ideally I'm looking for a solution that functions identically to the request example using axios, this works great for my use case as it includes file uploads, because of this, I want to avoid parsing the body and instead just forwarding it onto the next server.

Testing of the above routes was performed with postman

2 Answers

Changing the Axios request headers to the equivalent ones obtained from the original request, resolved the error for me.

Code:

    const axiosRequest = await axios({
        url: FORWARD_URL,
        method: 'POST',
        responseType: 'stream',
        data: req
    })

I have just copied the original request headers to the new request

    const axiosRequest = await axios({
        url: FORWARD_URL,
        method: 'POST',
        responseType: 'stream',
        data: req,
        headers: {
            ...req.headers
        }
    })

The following code worked for me for POSTing streams with content type multipart/form-data:

app.post('/test/0', async (req, res) => {
  const response = await axios.post(FORWARD_URL, req, {
    headers: {
      'content-type': req.headers['content-type'],
    },
  });

  return res.send(response.data);
})

In my case I did not need to set the responseType header to stream. This might depend on the response you are getting from the API.

What is important is:

  • posting the entire request object (not just the body!)
  • setting the content-type header of the outgoing request to the same value as the incoming request

Passing just the body of the incoming request to the outgoing request will result in the API (the reader of the stream) endpoint receiving an empty object (which it will initialise with default values).

Passing the body of the incoming request and setting the content-type header will result in the API endpoint receiving a null body.

Related