Im trying to save imageUploads to a folder "/images". My issue is that req.files seems to be undefined for some reason. I will try to send the code I think is relevant. Feel free to ask any questions, and any help will be much appreciated! 🙂
//check if/else block
Output: "No files"
Backend code:
adminRouter.post(
'/products/create',
upload.array('images', 5),
asyncHandler(async (req: Request, res: Response) => {
if (!req.body) {
res.status(400).json({ message: 'Product data not found' })
return
}
const {
name,
rating,
numReviews,
reviews,
slug,
price,
brand,
category,
countInStock,
description,
} = req.body
const images = (req.files as Express.Multer.File[]).map(
(file: Express.Multer.File) => file.path
)
if (req.files) {
console.log('Files:', req.files)
} else {
console.log('No files')
}
const product = new ProductModel({
name,
slug,
price,
brand,
category,
countInStock,
description,
images,
rating,
numReviews,
reviews,
})
try {
const createdProduct = await product.save()
res.status(201).json(createdProduct)
} catch (error) {
console.error('Error creating product:', error)
res.status(500).json({ message: 'Failed to create product' })
}
})
)
Frontend hook:
export const useCreateProductMutation = () => {
return useMutation({
mutationFn: async (product: {
name: string
slug: string
price: number
description: string
images: string[]
brand: string
category: string
rating: number
countInStock: number
numReviews: number
reviews: Review[]
}) => {
try {
const response = await apiClient.post<{ message: string }>(
`api/admin/products/create`,
product
)
return response.data
} catch (error) {
throw new Error('Failed to create product')
}
},
})
}
Frontend Form
export default function AdminCreateProductPage() {
const [formData, setFormData] = useState({
name: '',
price: 0,
images: [] as string[],
brand: '',
category: '',
countInStock: 0,
description: '',
slug: '',
rating: 0,
numReviews: 0,
reviews: [],
})
const { mutateAsync: createProduct } = useCreateProductMutation()
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
const newImages = Array.from(e.target.files).map((file) =>
URL.createObjectURL(file)
)
setFormData({ ...formData, images: [...formData.images, ...newImages] })
}
}
const submitHandler = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
createProduct(formData)
}
return (
<div>
<h1>Edit Product</h1>
...
...
<Form onSubmit={submitHandler} encType="multipart/form-data">
<Row className="w-100">
<Col md={8}>
<Form.Group controlId="images">
<Form.Label>Images</Form.Label>
<Form.Control
type="file"
name="images"
multiple
onChange={handleImageChange}
/>
</Form.Group>
...
...
I have tried to find answers online, and communicating with chatGPT. But I can’t see to come any close.
2
Answers
Multer only handle
multipart/form-data
and will ignoreapplication/json
You are probably just doing a post using a json object as body and not a FormData
Instead of
You will probably have to do something like
see https://developer.mozilla.org/en-US/docs/Web/API/FormData for more info
Content-type
is a header that help identify how the body of the request is formatted.Most of the time when you provide an object as body of your request, the client you are using will automatically fill
Content-type
withapplication/json
in the request that is sent to your serverBased on this header a middleware of your server will parse the body.
For file upload,
multipart/form-data
is often used, and multer middleware only look for that header.Multer is ignoring application/json, have to pass into multipart/form-data .