I want to create new room and save the image in cloudinary when creating a new room. However when I ran code, it appeared error {“error”:”next is not a function”}. My code is below:
api route (api/admin/room/route.js)
import { connect } from '@/backend/config/mongodb'
import { NextResponse } from 'next/server'
import slugify from 'slugify'
import Room from '@/backend/models/Room'
import { uploads } from '@/backend/utils/cloudinary'
import fs from 'fs'
import upload from '@/backend/utils/multer'
export const dynamic = "force-dynamic"
connect()
export const config = {
api: {
bodyParser: false,
},
}
const uploadMiddleware = upload.array("image")
export async function POST(req) {
console.log('???? ~ POST ~ req:', req)
try {
await uploadMiddleware(req)
console.log('???? ~ POST ~ req.body:', req.body)
const { name } = await req.body
console.log('???? ~ POST ~ name:', name)
const existingRoom = await Room.findOne({ name })
if (existingRoom) {
return new NextResponse("Room already exists", { status: 400 })
}
// Tải lên hình ảnh lên Cloudinary
const uploader = async (path) => await uploads(path, 'nhaxinh/rooms')
const file = req.files[0]
console.log('???? ~ POST ~ file:', file)
const { path } = file
const imageResponse = await uploader(path)
console.log('???? ~ POST ~ imageResponse:', imageResponse)
fs.unlinkSync(path)
const newRoom = new Room({
name,
slug: slugify(name),
image: imageResponse
})
const savedRoom = await newRoom.save()
return NextResponse.json(savedRoom, { status: 200 })
} catch (error) {
console.log('???? ~ POST ~ error:', error)
return new NextResponse(JSON.stringify({ error: error.message }), { status: 500 })
}
}
function call to api route above
import axios from 'axios'
export const createNewRoom = async (formData) => {
console.log('???? ~ createNewRoom ~ formData:', formData)
try {
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/room`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
console.log('???? ~ createNewRoom ~ res:', res)
const data = await res.data
console.log('???? ~ createNewRoom ~ data:', data)
return data
} catch (error) {
console.log(error)
}
}
**
code RoomForm.jsx
**
'use client'
import React, { useState } from 'react'
import TextField from '@mui/material/TextField'
import ImageUpload from './UploadImage'
import { createNewRoom } from '@/backend/services/admin/room'
import { toast } from 'react-toastify'
import Link from 'next/link'
import { Plus, Trash } from "lucide-react"
import Image from "next/image"
export default function RoomForm() {
const [name, setName] = useState('')
const [image, setImage] = useState('')
const [imagePreview, setImagePreview] = useState('')
const onChange = (e) => {
const file = e.target.files[0]
// setImage(file)
const reader = new FileReader()
reader.onload = () => {
setImagePreview(reader.result)
}
setImage(file)
reader.readAsDataURL(file)
}
const submitHandler = async (e) => {
e.preventDefault()
try {
if (!name || !image) {
toast.error('Vui lòng chọn hình ảnh cho phòng và đặt tên phòng!')
return
}
const formData = new FormData()
console.log('???? ~ submitHandler ~ formData:', formData)
formData.set('name', name)
formData.set('image', image)
const responseData = await createNewRoom(formData)
console.log('???? ~ submitHandler ~ responseData:', responseData)
if (responseData.error) {
toast.error(responseData.error)
} else {
toast.success(responseData.message)
}
} catch (error) {
toast.error('Error: ' + error.message)
}
}
const handleRemove = () => {
setImage('')
setImagePreview('')
}
return (
<div className='mx-10 my-5'>
<div className='text-3xl font-bold text-gray-600'>Tạo Danh Mục Phòng</div>
<div className='bg-grey-800 mt-4 mb-7 border-2'></div>
<form className='border-2 rounded-md p-4' onSubmit={submitHandler}>
<TextField id="outlined-basic" label="Tên phòng" variant="outlined" className='w-full mb-3' name='name' value={name} onChange={(e) => setName(e.target.value)} required /> <br />
<div className='mt-3 mb-4'>
<div className=''>Hình ảnh</div>
{/* <ImageUpload
// value={room.images ? [room.images] : []}
// onChange={(url) => setRoom({ ...room, images: [...room.images, url] })}
// onRemove={(urlToRemove) => setRoom({ ...room, images: room.images.filter(url => url !== urlToRemove) })}
required
onChange={handleImageChange}
/> */}
<div className="">
<div className="mb-4 flex flex-wrap items-center gap-4">
{imagePreview && (
<div className="relative w-[200px] h-[200px]">
<div className="absolute top-0 right-4 z-10">
<button
type="button"
onClick={handleRemove}
className="bg-red-500 p-2 rounded-md text-white"
>
<Trash className="h-4 w-4" />
</button>
</div>
<Image src={imagePreview} alt="collection" className="object-cover rounded-lg" fill />
</div>
)}
</div>
<div>
<label htmlFor="image-upload" className="bg-gray-700 rounded flex items-center py-2 px-3 text-white cursor-pointer">
<Plus className="h-4 w-4 mr-2" />
Upload Image
</label>
<input
type="file"
id="image-upload"
className="hidden"
accept="image/*"
onChange={onChange}
/>
</div>
</div>
</div>
<div className='flex items-center gap-4 mt-6'>
<button type='submit' className='bg-blue-500 text-xl text-white rounded py-3 px-4 hover:bg-blue-700'>Tạo mới</button>
<Link href='/admin/danh-muc-phong'>
<button type='submit' className='bg-red-500 text-xl text-white rounded py-3 px-4 hover:bg-red-700'>Quay lại</button>
</Link>
</div>
</form>
</div>
)
}
when I console.log ~ submitHandler ~ responseData is undefined
this is code of cloudinary and multer that I set up
**
cloudinary.js
**
import cloudinary from 'cloudinary'
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
})
const uploads = (file, folder) => {
return new Promise((resolve, reject) => {
cloudinary.uploader.upload(
file,
(result) => {
resolve({
public_id: result.public_id,
url: result.url,
})
},
{
resource_type: "auto",
folder: folder,
}
)
})
}
export { uploads, cloudinary }
**
multer.js
**
import multer from "multer"
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads')
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + '-' + file.originalname)
},
})
const fileFilter = (req, file, cb) => {
if (
file.mimetype === 'image/jpeg' ||
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg'
) {
cb(null, true)
} else {
({ error: "File định dạng không được hỗ trợ! Chỉ tải lên tệp JPEG/JPG hoặc PNG" }, false)
}
}
const upload = multer({
storage,
limits: { fieldSize: 1024 * 1024 },
fileFilter
})
export default upload