I’m currently learning Mongoose and ran into an issue with the findByIdAndUpdate() method. I have a form where I enter data, and this data is stored in a MongoDB database using Mongoose. Everything works fine when I manually enter the content and update it via the API.
However, when I copy and paste content from the web that contains HTML tags such as
, , etc., into the form and save it, the data is saved correctly to the database. But when I try to update this specific post, I encounter the following error:
Cast to ObjectId failed for value “undefined” (type string) at path “_id” for model “Post”.
I logged the formData._id and currentUser._id in the console, and it appears that formData._id is undefined, even though the post was saved in the database with a valid _id generated by MongoDB. This issue only happens with posts that contain HTML content, while others update without any problem.
Here’s the relevant part of code:
import { FileInput, Select, TextInput, Button, Alert } from 'flowbite-react'
import React, { useEffect, useState } from 'react'
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import {getDownloadURL, getStorage, ref, uploadBytesResumable} from 'firebase/storage';
import { app } from '../firebase';
import {CircularProgressbar} from 'react-circular-progressbar' ;
import 'react-circular-progressbar/dist/styles.css';
import { useNavigate, useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
export default function UpdatePost() {
const [file, setFile] = useState(null);
const [imageUploadProgress, setImageUploadProgress] = useState(null);
const [imageUploadError, setImageUploadError] = useState(null);
const [formData, setFormdata] = useState({});
const [publishError, setPublishError] = useState(null);
const {postId} = useParams();
const navigate = useNavigate();
const {currentUser} = useSelector(state => state.user);
useEffect(() =>{
try {
const fetchPost = async () => {
const res = await fetch(`/api/post/getposts?postId=${postId}`);
const data = await res.json();
if(!res.ok){
console.log(data.message);
setPublishError(data.message);
return;
}
if(res.ok) {
setPublishError(null);
setFormdata(data.posts[0]);
console.log("Fetched post data:", data.posts[0]);
}
};
fetchPost();
} catch (error) {
console.log(error.message);
}
}, [postId])
const handleUploadImage = async () => {
try {
if(!file){
setImageUploadError('Please select an image');
return;
}
setImageUploadError(null);
const storage = getStorage(app)
const fileName = new Date().getTime() + '-' + file.name;
const storageRef = ref(storage, fileName);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed', (snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) *100;
setImageUploadProgress(progress.toFixed(0));
}, (error) => {
setImageUploadError('Image upload fail!');
setImageUploadProgress(null);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((DownloadURL) => {
setImageUploadProgress(null);
setImageUploadError(null);
setFormdata({...formData, image: DownloadURL});
});
}
);
} catch (error) {
setImageUploadError('Image upload failed');
setImageUploadProgress(null);
console.log(">>> Check error: ", error);
}
};
const handleSubmit = async (event) => {
event.preventDefault();
console.log(">>> form data id: ",formData._id)
console.log(">>> currentUser id: ", currentUser._id);
try {
const res = await fetch(`/api/post/updatepost/${formData._id}/${currentUser._id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData),
});
const data = await res.json();
if(!res.ok){
setPublishError(data.message);
return;
}
if(res.ok){
setPublishError(null);
navigate(`/post/${data.slug}`);
}
} catch (error) {
setPublishError('Something went wrong');
}
}
return (
<div className='p-3 max-w-3xl mx-auto min-h-screen'>
<h1 className='text-center text-3xl my-7 font-semibold '>
UPDATE YOUR POST
</h1>
<form className='flex flex-col gap-4 ' onSubmit={handleSubmit}>
<div className="flex flex-col gap-4 sm:flex-row justify-between">
<TextInput type='text' placeholder='Title' required id='title' className='flex-1'
onChange={(event) => setFormdata({...formData, title: event.target.value})}
value={formData.title}
/>
<Select value={formData.category} onChange={(event) => setFormdata({...formData, category: event.target.value})}>
<option value='uncategorized'>Select a category</option>
<option value='javascript'>JavaScript</option>
<option value='reactjs'>React.js</option>
<option value='nextjs'>Next.js</option>
</Select>
</div>
<div className="flex gap-4 items-center justify-between border-4 border-teal-300 border-dashed p-3">
<FileInput type='file' accept='image/*' onChange={(event)=>setFile(event.target.files[0])}/>
<Button type='button' gradientDuoTone='purpleToBlue' size='sm' outline onClick={handleUploadImage} disabled={imageUploadProgress}>
{
imageUploadProgress ?
<div className="w-16 h-16">
<CircularProgressbar value={imageUploadProgress} text={`${imageUploadProgress||0}%`}/>
</div> :
( 'Upload Image')
}
</Button>
</div>
{
imageUploadError && (
<Alert color='failure'>
{imageUploadError}
</Alert>
)
}
{formData.image && (
<img src={formData.image} alt="upload" className='w-full h-72 object-cover'/>
)}
<ReactQuill required
value={formData.content}
theme='snow'
placeholder='Write something in your mind . . .'
className='h-72 mb-12'
onChange={(value) => setFormdata({...formData, content: value})}
/>
<Button type='submit' gradientDuoTone='tealToLime'><div className="text-lg text-purple-800">Update</div></Button>
{
publishError && <Alert color='failure' className='mt-5'>{publishError}</Alert>
}
</form>
</div>
)
}
My model:
import mongoose from "mongoose";
const postChema = new mongoose.Schema({
userId: {
type: String,
required: true
},
content: {
type: String,
required: true,
} ,
title: {
type: String,
required: true,
unique: true
},
image: {
type: String,
default: 'https://bs-uploads.toptal.io/blackfish-uploads/components/blog_post_page/8969409/cover_image/optimized/unnamed-76880e314ca9bcaa0e0e19be57d2e75d.png'
},
category: {
type: String,
default: 'uncategorized',
},
slug: {
type: String,
required: true,
unique: true
},
}, {timestamps:true});
const Post = mongoose.model('Post', postChema);
export default Post;
I also came across a solution where it’s suggested to use mongoose.Types.ObjectId to handle such issues, but I’m unsure how to apply this to my case, especially when dealing with data that includes HTML content.
My question is:
Why is formData._id becoming undefined when the post includes HTML content?
How can I correctly update such posts without encountering this casting error?