Abdallah Safieddine
Abdallah Safieddine

Reputation: 125

Nextjs 13 Formidable parse function have no response and stocked

Formidable is stocked with no response at all, the api is always pending forever with no lock finding solution for weeks.

I've created a simple profile page with image inside a form to upload it to api handler in next js, inorder to have a full stack web app, but it is always in the pending state:

Here is the front-end code:

 import React, {FormEvent, useRef, useState} from "react";
 import {useSession} from "next-auth/react";
 import Paper from "@mui/material/Paper";
 
 import {Avatar, Box, Button, Grid, TextField, Typography,} from "@mui/material";
 import FormData from "form-data";
 
 
 const Profile = () => {
 
   const {data: session} = useSession();
 
   const filePathRef = useRef<HTMLInputElement | null>(null);
 
   const initialState = {
     firstName: session?.user.firstname == null ? "" : session?.user.firstname,
     lastName: session?.user.lastname == null ? "" : session?.user.lastname,
     currentPassword: "",
     profilePicUrl: session?.user.profileImage == null ? "" : session?.user.profileImage
   }
 
   // @ts-ignore
   const handleImageChange = (e) => {
     const file = e.target.files[0];
     if (file) {
       const reader = new FileReader();
 
       reader.onloadend = () => {
         const uri = URL.createObjectURL(file)
         setFormData((prevState) => ({
           ...prevState,
           profilePicUrl: uri
         }));
         setFile(file)
       };
       reader.readAsDataURL(file);
     } else {
       setFormData((prevState) => ({
         ...prevState,
         profilePicUrl: ""
       }));
     }
   };
 
   const [formData,
     setFormData] =
       useState(initialState);
 
   const [file,
     setFile] =
       useState<File | null>(null);
 
   const requestState = {
     isError: false,
     message: ""
   }
 
   const [state,
     setState
   ] = useState(requestState)
 
   // @ts-ignore
   const handleFormChange = (event) => {
     const {name, value} = event.target;
     setFormData((prevState) => ({
           ...prevState,
           [name]: value,
         }));
   };
 
   function isFormChanged() {
     return formData.firstName != initialState.firstName ||
         formData.lastName != initialState.lastName ||
         formData.profilePicUrl != initialState.profilePicUrl
   }
 
   const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
     console.log("dasdas")
 
     e.preventDefault();
     if (isFormChanged()) {
 
       const data = new FormData()
       console.log("formData " + formData)
 
       if (file) {
         data.append("firstName", formData.firstName)
         data.append("lastName", formData.lastName)
         data.append("password", formData.currentPassword)
       }
 
       if (file != null) { // @ts-ignore
         data.append("file", file)
       }
 
 
       const response = await fetch(
           '/api/admin', {
             method: 'POST',
             body: data
           });
 
       if (response.ok) {
         setState((prevState) => ({
           ...prevState,
           isError: false,
           message: 'Update successful!'
         }));
       } else {
         setState((prevState) => ({
           ...prevState,
           isError: true,
           message: 'Update failed wrong current password'
         }));
       }
     }
   };
 
   return (
       <>
         <h1>Profile</h1>
         <Box>
           <Typography variant={"h4"} sx={{paddingBottom: 4}}>
             Hey {session ? session?.user?.firstname + " " + session?.user?.lastname : "User"}, welcome to your profile
             👋
           </Typography>
 
           <Paper sx={{padding: "1rem 2rem"}}>
             <Grid container justifyContent="center">
               <Grid item xs={12} sm={8} md={6}>
                 <Box display="flex" flexDirection="column" alignItems="center">
                   <Avatar
                       sx={{
                         height: 100,
                         width: 100,
                         marginBottom: 2,
                       }}
                       onClick={() => {
                         filePathRef.current?.click()
                       }}
                       src={formData.profilePicUrl}
                   />
                   <Typography variant={"h6"} sx={{
                     paddingBottom: 4,
                     color: state.isError ? 'red' : 'green'
                   }}>
                     {state.message}
                   </Typography>
                 </Box>
                 <form
                     method="post"
                     onSubmit={handleSubmit}
                     style={{maxWidth: 600, margin: "0 auto"}}>
                   <Grid container spacing={3}>
                     <Grid item xs={12} sm={6}>
                       <TextField
                           required
                           fullWidth
                           label="First Name"
                           name="firstName"
                           value={formData.firstName}
                           onChange={handleFormChange}
                       />
                     </Grid>
 
                     <Grid item xs={12} sm={6} hidden>
                       <input
                           name="image"
                           type="file"
                           id="image"
                           ref={filePathRef}
                           onChange={handleImageChange}
                       />
                     </Grid>
 
                     <Grid item xs={12} sm={6}>
                       <TextField
                           required
                           fullWidth
                           label="Last Name"
                           name="lastName"
                           value={formData.lastName}
                           onChange={handleFormChange}
                       />
                     </Grid>
                     <Grid item xs={12}>
                       <TextField
                           required
                           fullWidth
                           type="password"
                           label="Current Password"
                           name="currentPassword"
                           value={formData.currentPassword}
                           onChange={handleFormChange}
                       />
                     </Grid>
 
                     <Grid item xs={12}>
                       <Button type="submit" variant="contained" color="primary">
                         Save Changes
                       </Button>
                     </Grid>
                   </Grid>
                 </form>
               </Grid>
             </Grid>
           </Paper>
         </Box>
       </>
   );
 };
 
 
 export default Profile;

Backend handler:

    import type { NextApiRequest, NextApiResponse } from "next";
    import { IncomingForm } from "formidable";
    
    export default async function handle(request:NextApiRequest,response:NextApiResponse) {
        try {
            console.log(JSON.stringify(request.body))
            await new IncomingForm().parse(request)
            response.status(200).json({b: ""})
        } catch (e) {
            console.log("error " + e)
            response.status(500).json({b: ""})
        }
    }
    
    export const config = {
        Api: {
            bodyParser: false,
        },
    };

I'm trying just to return a reponse with no luck.

Upvotes: 0

Views: 162

Answers (1)

Mohammad Ahmer Malick
Mohammad Ahmer Malick

Reputation: 63

You don't need Formidable to handle file in NextJS 13 as it handle it out of the box

As you are creating a new project it's recommended to use app/ directory.

Here I have create a similar form which submits on "/api/home"

const Home = () => {
  return (
    <form
      method="post"
      action="/api/home"
      style={{ color: "gray" }}
      encType="multipart/form-data"
    >
      <div>
        First Name <input type="text" name="firstName" defaultValue="John" />
      </div>
      <div>
        Image <input type="file" name="image" />
      </div>
      <div>
        Last Name <input type="text" name="lastName" defaultValue="Doe" />
      </div>
      <div>
        Current Password{" "}
        <input type="password" name="currentPassword" defaultValue="password" />
      </div>
      <button
        type="submit"
        style={{ border: "1px solid gray", padding: "4px" }}
      >
        Submit
      </button>
    </form>
  )
}

export default Home

and here we are handling that form data

import path from "path"
import { writeFile } from "fs/promises"

export const POST = async (request: Request) => {
  const formData = await request.formData()

  // here we are getting form fields
  const lastName = formData.get("lastName")
  const firstName = formData.get("firstName")
  const image = formData.get("image") as File
  const currentPassword = formData.get("currentPassword")

  let file = {}

  // if image is present
  if (image?.name) {
    // then store the image on local
    const fileLocalPath = path.join(process.cwd(), `public/${image.name}`)
    const buffer = Buffer.from(await image.arrayBuffer())
    await writeFile(fileLocalPath, buffer)

    // file information
    file = {
      size: image.size,
      filepath: fileLocalPath,
      newFilename: image.name,
      type: image.type,
      lastModified: image.lastModified,
    }
  }

  return Response.json({ lastName, firstName, image: file, currentPassword })
}

it will give you response like this

Nextjs file upload response

Upvotes: 0

Related Questions