'FormData with NextJS API

Background

I am trying to create a simple CRUD application using NextJS along with react-redux, so what it does is that it saves peoples contacts.So when adding a contact i am trying to send some data along with a file to a NextJS API.

Issue

ContactAction.js

Make a POST request from redux action to add a contact

export const addContact = (data) => async (dispatch) => {
 try {
      var formData=new FormData();
      formData.append('name',data.Name);
      formData.append('email',data.Email);
      formData.append('phone',data.Phone);
      formData.append('image',data.Image);
      let response= await Axios.post(`http://localhost:3000/api/contact/addContact`,formData,{
         headers:{
             'x-auth-token':localStorage.getItem('token')
         }
      });
   } catch (error) {
       console.log(error);
   }
}

addContact.js

This is the API route in /api/contact/

  const handler = async (req, res) => {
   switch(req.method){
    case "POST":{
       await addContact(req,res)
       }
     }
   }
   const addContact = async  (req, res) => { 
     console.log(req.body);
    // do some stuff here and send response
   }

this is what i get in the terminal after the log,also the file is Gibberish as well when logging req.files

Terminal

Current Effort

I tried using third party packages such as formidable and formidable-serverless but got no luck. so after a day i made it work with a package called multiparty.

addContact.js

  const handler = async (req, res) => {
   switch(req.method){
    case "POST":{
      let form = new multiparty.Form();
      let FormResp= await new Promise((resolve,reject)=>{
       form.parse(req,(err,fields,files)=>{
        if(err) reject(err)
        resolve({fields,files})
        });
      }); 
      const {fields,files} = FormResp;
      req.body=fields;
      req.files=files;
       await addContact(req,res)
       }
     }
   }
   const addContact = async  (req, res) => { 
     console.log(req.body); //Now i get an Object which i can use
    // do some stuff here and send response
   }

The above solution is obviously redundant and probably not the best way to go about it plus i don't want to add these 7 8 lines into each route.

so if someone could help me understand what i am doing wrong and why formData doesn't seem to work with NextJS API (when it works with the Express server) i would be grateful.



Solution 1:[1]

FormData uses multipart/form-data format. That is not a simple POST request with a body. It is generally used for uploading files, that's why it needs special handling. As an alternative, you could use JSON.

Solution 2:[2]

Here is my solution, i hope this helps anybody.

First of all you need to install next-connect and multer as your dependencies.

Now you can use this API route code.

import nextConnect from "next-connect";
import multer from "multer";

const apiRoute = nextConnect({
  onError(error, req, res) {
    res.status(501).json({ error: `Sorry something Happened! ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method "${req.method}" Not Allowed` });
  },
});

apiRoute.use(multer().any());

apiRoute.post((req, res) => {
  console.log(req.files); // Your files here
  console.log(req.body); // Your form data here
  // Any logic with your data here
  res.status(200).json({ data: "success" });
});

export default apiRoute;

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
};

Solution 3:[3]

Here is an example about uploading file with Next.js:

https://codesandbox.io/s/thyb0?file=/pages/api/file.js

The most important code is in pages/api/file.js

import formidable from "formidable";
import fs from "fs";

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

const post = async (req, res) => {
  const form = new formidable.IncomingForm();
  form.parse(req, async function (err, fields, files) {
    await saveFile(files.file);
    return res.status(201).send("");
  });
};

const saveFile = async (file) => {
  const data = fs.readFileSync(file.path);
  fs.writeFileSync(`./public/${file.name}`, data);
  await fs.unlinkSync(file.path);
  return;
};

Generally speaking,in your api file,you should disable the default bodyParser,and write your own parser

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ivan V.
Solution 2 Vladislav Gritsenko
Solution 3