reactjsfile-uploadnext.jsformidable

next.js file upload via api routes / formidable - not working


I'm having a hell of a time getting file upload to work via api routes.

On client-side im submitting the file like so:

 onFormSubmit = (e) => {
    e.preventDefault() // Stop form submit

    this.fileUpload(this.state.file).then((response) => {
      console.log('rD', response.data)
    })
 }

 onFileChange = (e) => {
    this.setState({ file: e.target.files[0] })
 }

 fileUpload = (file) => {
    const url = '/api/mail/upload'
    const formData = new FormData()
    formData.append('file', file)
    const config = {
      headers: {
        'X-CSRF-TOKEN': this.props.session.csrfToken
      }
    }
    return axios.post(url, formData, config)
 }

My request to /api/mail/upload then looks like this:

Request Headers:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,de-DE;q=0.8,de;q=0.7
Connection: keep-alive
Content-Length: 1331
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBlNt6z8t4rGZT0x6
Cookie: abc123
Host: localhost:3000
Origin: http://localhost:3000
Referer: http://localhost:3000/new
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36
X-CSRF-TOKEN: abc123

Form Data:
file: (binary)

Then in the route (/api/mail/upload) I'm trying to use formidable to parse the form data and finally do something with the file.

I've made sure to disable the built-in body parser by including the following at the bottom of the api route file:

export const config = {
  api: {
    bodyParser: false
  }
}

^^ Thats the correct way to do that, right?

Finally, in the api route, I've tried many different things, but currently the following is what I expect to work, but it is not..

module.exports = async (req, res) => {
  const form = new formidable.IncomingForm()

  form.parse(req, (err, fields, files) => {
    if (err) return reject(err)
    console.log(fields, files)
    res.status(200).json({ fields, files })
  })
  // if I console.log(form) here - I can see the request details, so it seems to be picking that up
}

This generates no output whatsoever on the server-side nor on client-side, i expect the console.log(fields, files) to output on the server-side the name of the file, etc.

Anyone know what I am missing?


Solution

  • I am 100% sure your issue is caused by intermixing CommonJS exports (i.e. module.exports = ) and ES6 modules syntax (export const config = ...).

    When you do this NextJS sees only default export and doesn't see your config declaration and hence continues to parse body. Which, in turn, results in req to be already completed by the time it's being passed to formidable.

    Apart from that everything looks right in your code. By just changing

    module.exports = async (req, res) => {
    

    to

    export default async (req, res) => {
    

    you should get an expected behaviour.