Abdulkerim Awad
Abdulkerim Awad

Reputation: 131

Getting error: Failed to parse body as FormData in Next.js 14 app router

I have a Next 14 app router project, so the problem is in the dashboard page and in a more clear way in the add and update form, I'm using Formik to handle the forms and then I send the data as FormData to a server action which sends the data again to a route handler in FormData formate like:

Client

const handleSubmit = async (values: t_formValues, submitProps: FormikHelpers<t_formValues>): Promise<void> => {
    const formData: FormData = new FormData();
    
    if (values && values.cover) {
      // Stringify title, overview, description
      const _values = {...values, title: JSON.stringify(values.title), overview: JSON.stringify(values.overview), description: JSON.stringify(values.description)};
      formData.append("files.cover", values.cover as File);
      formData.append("data", JSON.stringify(Object.fromEntries(Object.entries(_values).filter(([key, value]) => key !== "cover"))));
    }

      const json = await Services.updateOne(serviceID, formData);
}

Server Action

const updateOne = async (serviceID: number, serviceDetails: FormData): Promise<{ data: t_service } | { data: null, errorCode: t_errorCode }> => {
    const res = await fetch(`${process.env.NEXT_PUBLIC_DOMAIN}/api/services`, {
      method: "PUT",
      headers: {
        "serviceID": serviceID.toString()
      },
      body: serviceDetails,
    })
    const json: { data: t_service, error?: Error } = await res.json();

    if (!res.ok) {
      throw json.error;
    }

    return { data: json.data };
}

Route handler

export async function PUT(request: Request) {
    const headersList = headers();
    const serviceID = headersList.get("serviceID");
    const formData: FormData = await request.formData();
 
    const res = await fetch(`${process.env.API_ENDPOINT}/services/${serviceID}`, {
      method: "PUT",
      headers: {
        "Authorization": `Bearer ${process.env.NEXT_SERVER_REST_API_KEY}`,
      },
      body: formData,
    });
    
    const json = await res.json();
    
    if (!res.ok) {
      throw json.error;
    }
    
    return Response.json({
      ...json
    }, { status: res.status })
}

After submitting I'm getting the following error in Vercel logs

TypeError: Failed to parse body as FormData.
    at node:internal/deps/undici/undici:4249:27
    at successSteps (node:internal/deps/undici/undici:4288:27)
    at fullyReadBody (node:internal/deps/undici/undici:2724:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async consumeBody (node:internal/deps/undici/undici:4297:7)
    at async m (/var/task/.next/server/app/api/services/route.js:1:3102)
    at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:42484
    at async eI.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:32486)
    at async eI.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:43737)
    at async Y (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:16:24556)

I expected it to work correctly like the development environment but the error above thrown in the production env

Edit

I have noticed that the "data" field is sent correctly from the client side but arrived at the server action without the last 25% of its content, I don't know if this stuff caused the error

Upvotes: 5

Views: 2608

Answers (4)

Austine
Austine

Reputation: 76

This is kind of a slide out but still helpful. I realized that if you have a "Content-type": "multipart/form-data" or "Content-type": ""application/x-www-form-urlencoded" been set in the header, removing it will solve the issue, the form submits and passes it own headers content-type.

Upvotes: 4

Xiel
Xiel

Reputation: 61

I realized that it has something to do with node's version with undici which is used to parse the formData. I suggest upgrading the node version. In my case, I upgraded to v20.12.2

Spent a solid 8 hours debugging this 💀

Upvotes: 2

Murat Yasar
Murat Yasar

Reputation: 1044

I ended up quitting NextJS as a backend, starting a new NodeJS project from scratch unfortunately..

Upvotes: 3

Abdulkerim Awad
Abdulkerim Awad

Reputation: 131

I found a temporary solution until someone finds a complete solution: The error is solved by moving the API requests from the Route handler to the server actions where the request body is passed completely without shortages.

Upvotes: 1

Related Questions