Next js 14 api handling multipart/formData issue

278 Views Asked by At

So i am using next js 14 and i have a component that uploads an image and send it to the api in the next-api. I am using formData and i have also set the request headers to "multipart/form data". The code is as follows:

const handleUpload = async (e: any) => {
    const formData = new FormData();
    formData.append("image", e?.target?.files[0]);
    formData.append("name", e?.target?.files[0]?.name);

    try {
      const response = await fetch(`/api/cards/${id}/uploadAttachment/`, {
        method: "POST",
        body: formData,
        headers: {
          "Content-Type": "multipart/form-data",
        },
      });
      if (response.ok) {
        const data = await response.json();
        console.log("Response Data", data);
      } else {
        console.log("error", data);
        toast.error("Error! in else");
        throw new Error("Failed to make POST request");
      }
    } catch (error) {
      toast.error("Error" + error);
      toast.error("Error! in else");
    }
  };

And i am trying to access this image in the api route.ts file, i have used formidable and other way arounds but nothing helps. with formidable it gives me an error "request.on is not a function". Here is the code of the route:

import { NextResponse, NextRequest } from "next/server";
import formidable from "formidable";

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

export async function POST(
  req: any,

  { params }: { params: { cardId: string } }
) {
  try {
    const options: formidable.Options = {};
    options.maxFileSize = 4000 * 1024 * 1024;
    options.keepExtensions = true;

    const form = formidable(options);
    return new Promise((resolve, reject) => {
      form.parse(req, (err, fields, files) => {
        if (err) reject(err);
        resolve({ fields, files });
      });
    });
  } catch (error) {
    return new NextResponse("Internal Error", { status: 500 });
  }
}

I tried formidable and it throws an error saying req.on is not a function. Have not been able to fix this issue since.

UPDATE: I fixed the issue by removing the multipart/form-data header in request and also changing the req type to Request (NodeJS.fetch._Request) and then in the api route.ts used req.formData() like this:

const formData = await req.formData();
1

There are 1 best solutions below

1
Hashan Hemachandra On

When you're using FormData to send files and data to the server, you should not set the "Content-Type" header manually. This is because the browser will automatically set the correct "Content-Type" header for you, including the boundary parameter necessary for parsing multipart forms.

Adjust your client code as follows:

const handleUpload = async (e) => {
    const formData = new FormData();
    formData.append("image", e.target.files[0]);
    formData.append("name", e.target.files[0].name);

    try {
        const response = await fetch(`/api/cards/${id}/uploadAttachment/`, {
            method: "POST",
            body: formData,
            // Remove the Content-Type header
        });
        if (response.ok) {
            const data = await response.json();
            console.log("Response Data", data);
        } else {
            const errorData = await response.text(); // Change to get the response text
            console.log("error", errorData);
            toast.error("Error!");
        }
    } catch (error) {
        console.log("Error", error); // Log the actual error
        toast.error("Upload failed: " + error.message);
    }
};

For the server side code make sure your API route is not an Edge function as formidable expects a Node.js IncomingMessage object. Also, ensure that your API route is correctly structured for handling multipart data with formidable. You're correctly disabling bodyParser, which is necessary.

Here is the server side code:

import { NextApiRequest, NextApiResponse } from 'next';
import formidable from 'formidable';

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

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    const options: formidable.Options = {
      maxFileSize: 4000 * 1024 * 1024, // 4GB
      keepExtensions: true,
    };

    const form = new formidable.IncomingForm(options);

    form.parse(req, (err, fields, files) => {
      if (err) {
        console.error(err);
        return res.status(500).json({ error: 'Something went wrong during the file upload.' });
      }
      // Process your fields and files here
      res.status(200).json({ fields, files });
    });
  } else {
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}