Skip to content

How I proxied a file upload through a Next JS middleware

The short answer, it turns out that Axios knows what to do with a Buffer object.

a paper ball next to a paper plane

I needed to upload a file from a browser to middleware through an API call, and then have the middleware also send said file to a service through an API call. The service expects the payload to be exactly the file data, no enveloping.

I own the middleware and client code and here’s the solution that I set up to correctly call the service API. I didn’t conduct thorough research on this approach but it worked for me.

The popular solution for handling file upload in Next.js land is a Multer middleware with Next-Connect for simplified integration. For simplicity, I opted to just use Busboy to process the body payload. As I discovered, it turns out that Multer also leverages Busboy.

The Client

I’m expecting the client to be a modern browser so I can presume the fetch interface to be present. I might use Axios in the client to streamline with Node server coding as a future consideration. To bundle the image in my API call, I use FormData for convenience. I’ve discussed using FormData as an AJAX version of submitting a form and it is well supported.

// get the File object from a file uploader event
const file = e.target.files[0]

// use FormData to build the payload as multipart/form-data
const blobData = new FormData()
blobData.append('image', file)

// make api call with image payload
fetch(route, {
  method: 'POST',
  body: blobData,
})

The Middleware

The middleware runs on a Next.js server. For this example, assume the existence of the req argument. Next.js automatically processes the request body so I had to disable that to allow for Busboy to stream the data.

I encountered a challenge in extracting a correctly encoded image data payload in isolation. Through experimentation I discovered that Axios knows what to do with a Buffer as a data argument.

Code updated to Busboy 1.0 compatibility – see breaking changes.

// don't process the body - busboy will handle that
export const config = {
  api: {
    bodyParser: false
  }
}

// process payload inside a request handler

// configure busboy to only look for one file
const busboy = Busboy({
  headers: req.headers,
  limits: {
    files: 1
  }
})

// process the request body and stream the file data
busboy.on("file", (fieldname, file, {filename, encoding, mimetype}) => {
  const buf = []
  file.on("data", (d) => {
    buf.push(d)
  ).on("end", async () => {
    const data = Buffer.concat(buf)
      axios.post('/api-route', data, {headers: {'Content-Type': 'image/*'}})
  })
})

// pipe the request through the busboy middleware
req.pipe(busboy)