I already tested the code in postman and works, but when i do it through React it tells me 400 and passes the validation i created the field title is required. I also checked form data and is being received correctly. The problem is that on submit req.body is undefined.
index.js
index.js
import express from "express";
import cors from "cors"
import bodyParser from "body-parser"
import mongoose from "mongoose";
import dotenv from "dotenv";
import userRoutes from "./routes/user.route.js";
import authRoutes from "./routes/auth.route.js";
import courseRoutes from "./routes/course.route.js";
import cookieParser from "cookie-parser";
import path from "path";
dotenv.config();
mongoose
.connect(process.env.DB_URI)
.then(() => {
console.log("Connected to MongoDB");
})
.catch((err) => {
console.log(err);
});
const __dirname = path.resolve();
//! BASIC CONFIG OF APP
const app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }));
const corsOptions = {
origin: "*",
credentials: true,
optionSuccessStatus: 200
}
app.use(cors(corsOptions))
app.use(cookieParser());
// Serve static files from the 'uploads' directory
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
// routes
app.use("/api/user", userRoutes);
app.use("/api/auth", authRoutes);
app.use("/api/course", courseRoutes);
// error formatter for console
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
const message = err.message || "Internal Server Error";
return res.status(statusCode).json({
success: false,
message,
statusCode,
});
});
app.listen(process.env.PORT, () => {
console.log("Server listening on port", process.env.PORT);
});
export default app
CourseUpdate.js
import React, { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
const CourseUpdate = () => {
const { currentUser } = useSelector((state) => state.user);
const { id } = useParams();
const [errorMessage, setErrorMessage] = useState("");
const [course, setCourse] = useState(null);
const [successMessage, setSuccessMessage] = useState("");
const renderImage = (formData) => {
const file = formData.get("image");
const image = URL.createObjectURL(file);
$image.current.setAttribute("src", image);
};
const $image = useRef(null);
useEffect(() => {
// Fetch course data when the component mounts
const fetchCourse = async () => {
try {
const response = await fetch(`http://localhost:3006/api/course/${id}`);
if (response.ok) {
const data = await response.json();
setCourse(data.course);
} else {
throw new Error("Failed to fetch course data");
}
} catch (error) {
console.error("Error fetching course:", error);
setErrorMessage("Failed to fetch course data");
}
};
fetchCourse();
}, [id]);
const handleChange = (e) => {
const { name, value } = e.target;
setCourse((prevCourse) => ({
...prevCourse,
[name]: value,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const imageFile = formData.get("image");
const videoFile = formData.get("video");
if (imageFile) {
formData.set("image", imageFile, imageFile.name);
}
if (videoFile) {
formData.set("video", videoFile, videoFile.name);
}
formData.append("title", course.title);
renderImage(formData);
const response = await fetch(
`http://localhost:3006/api/course/update/${id}`,
{
method: "PUT",
body: formData,
}
);
if (response.ok) {
const data = await response.json();
setSuccessMessage(encodeURIComponent(data.message));
window.location.href = data.redirectUrl;
} else {
const errorData = await response.json();
setErrorMessage(errorData.message);
}
};
// Render the form only when course data is fetched
return (
course && (
<div id="update-course-container-page">
{errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
{successMessage && <p style={{ color: "green" }}>{successMessage}</p>}
<div id="update-course-container" className="mt-8 mx-5">
{currentUser && <p className="text-primary">Hello, {currentUser._id}!</p>}
<h1 className="section-title">Actualizando Curso</h1>
<div>
<form className="form" method="POST" onSubmit={handleSubmit}>
{errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
{/* CONTENT */}
<h3>Titulo & contenido:</h3>
<div className="row">
<div className="col-lg-6">
<label htmlFor="title">titulo:</label>
<input
name="title"
value={course.title}
onChange={handleChange}
type="text"
className="form-control"
/>
</div>
<div className="col-lg-6">
<label htmlFor="description">Descripcion:</label>
<input
name="description"
value={course.description}
onChange={handleChange}
type="text"
className="form-control"
/>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<label htmlFor="text_content">contenido texto:</label>
<textarea
name="text_content"
value={course.text_content}
onChange={handleChange}
type="text"
className="form-control"
></textarea>
</div>
</div>
<hr />
{/* UPLOAD */}
<h3>Subir Archivos:</h3>
{errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
<div className="row col-lg-12 items-center">
<div className="">
<label htmlFor="video">subir video :</label>
<input
type="file"
name="video"
accept="video/*"
onChange={handleChange}
/>
</div>
<br />
<br />
<div className="">
<label htmlFor="image">subir miniatura:</label>
<input
type="file"
id="file"
name="image"
accept="image/*"
onChange={handleChange}
/>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div className="preview">
<img id="img" ref={$image} style={{ width: 300 }} />
</div>
</div>
</div>
<hr />
{/* PRICE */}
<h3>Configurar precio</h3>
{errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
<div className="row col-lg-12 items-center">
<div className="">
<label htmlFor="ars_price">ARS Price:</label>
<input
type="number"
id="ars_price"
name="ars_price"
value={course.ars_price}
onChange={handleChange}
/>
</div>
<div className="">
<label htmlFor="usd_price">USD Price:</label>
<input
type="number"
id="usd_price"
name="usd_price"
value={course.usd_price}
onChange={handleChange}
/>
</div>
</div>
<hr />
{/* DISCOUNT */}
<h3>Adicionales & descuentos</h3>
{errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
<div className="row items-center col-lg-12">
<div className="">
<label htmlFor="discount_ars">
descuento_ars opcional (numeros enteros):
</label>
<br />
<strong>% ARS </strong>
<input
type="number"
id="discount_ars"
name="discount_ars"
value={course.discount_ars}
onChange={handleChange}
/>
</div>
<div className="">
<label htmlFor="discount_usd">
descuento_usd opcional (numeros enteros):
</label>
<br />
<strong>% USD </strong>
<input
type="number"
id="discount_usd"
name="discount_usd"
value={course.discount_usd}
onChange={handleChange}
/>
</div>
</div>
<input
type="hidden"
name="author"
value={currentUser._id}
/>
<br />
<br />
{/* submit */}
<div className="items-center text-center mt-20">
<button type="submit" className="btn btn-success">
Update Course
</button>
</div>
</form>
</div>
</div>
</div>
)
);
};
export default CourseUpdate;
course.model.js
import mongoose from 'mongoose';
const courseSchema = new mongoose.Schema(
{
title: { type: String, required: true },
slug: { type: String, required: true, unique: true },
description: { type: String, default: null },
text_content: { type: String, default: null },
ars_price: { type: Number, required: true },
usd_price: { type: Number, required: true },
discount_ars: { type: Number, default: null },
discount_usd: { type: Number, default: null },
thumbnail: { type: String, default: null, required:false },
video: { type: String, default: null, required:false }, // Including the video field
author_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, // Assuming there's a User model
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
},
{ timestamps: true }
);
const Course = mongoose.model('Course', courseSchema);
export default Course;
course.controller.js
// Update
export const courseUpdate = async (req, res, next) => {
try {
const courseId = req.params.id;
// Fetch existing course from the database
const course = await Course.findById(courseId);
if (!course) {
return next(errorHandler(400, `Course not found.`));
}
// Extract necessary data from request body
const {
title,
description,
text_content,
ars_price,
usd_price,
discount_ars,
discount_usd,
author,
} = req.body;
console.log("req.body",req.body)
// Manage discount value
const discountArs = discount_ars || null;
const discountUsd = discount_usd || null;
// Update course details
const updatedCourse = await Course.findByIdAndUpdate(courseId, {
title,
description,
text_content,
ars_price,
usd_price,
discount_ars: discountArs,
discount_usd: discountUsd,
});
await updatedCourse.save();
console.log("\nUpdating course...");
console.log("\nCourse:", updatedCourse);
// Redirect after updating the course
return res.status(200).json({
message: "Course updated successfully",
course: updatedCourse._id,
redirectUrl: `/course/${courseId}`
});
} catch (error) {
return next(error);
}
};
course.route.js
import express from "express";
import {
courseCreate,
courseUpdate,
courselist,
courseOwned,
courseDetail,
courseDelete,
} from "../controllers/course.controller.js";
import upload from "../useMulter.js";
const router = express.Router();
router.post(
"/create",
upload.fields([
{ name: "image", maxCount: 1 },
{ name: "video", maxCount: 1 },
]),
courseCreate
);
router.put("/update/:id", courseUpdate);
router.get("/all", courselist);
router.get("/owned", courseOwned);
router.get("/:id", courseDetail);
router.delete("/delete/:id", courseDelete);
export default router;