form-datanext.js14app-routerserver-action

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


Solution

  • 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.