First of all I tried to look for a solution to this specific issue in stackOverflow but i didn't find an answer for it.
The suggested link (POST Request with Fetch API?) doesn't answer my question. So please reopen the question.
The problem is with fetching an API endpoint from an express server. "GET" requests succeed while "POST" requests fail from the frontend (React). I should mention that there is no problem at all when i try fetching the API with POSTMAN, everything works as it should be.
Here is the code snippet :
BACKEND (Express JS) :
1. app.js
require('dotenv').config();
const express = require("express");
const mongoose = require("mongoose");
const { urlencoded } = require("express");
const WorkoutRoutes = require("./Routes/workouts");
const cors = require('cors');
// Start the App
const app = express();
app.use(cors());
// Must be specified before the Routes
app.use(express.json());
// The routes
app.use("/api/workouts", WorkoutRoutes);
// For Form Data
app.use(urlencoded({extended : true}));
// Connect to MongoDB using Mongoose
mongoose.set("strictQuery", false);
mongoose.connect(process.env.DB_URI, {useNewUrlParser: "true",useUnifiedTopology: "true"})
.then(()=>{
app.listen(process.env.PORT, ()=>{
console.log("app started on Port" + process.env.PORT);
})
});
2. Workouts Routes :
const express = require("express");
const mongoose = require("mongoose");
const router = express.Router();
const workoutscontrollers = require("../Controllers/workoutsControllers");
// Get All Workouts
router.get("/", workoutscontrollers.getAllWorkouts);
// Get a single Workout
router.get("/:id", workoutscontrollers.deleteAsingleWorkout);
// Post a new Workout
router.post("/", workoutscontrollers.postAnewWorkout);
// Delete a Workouts
router.delete("/:id", workoutscontrollers.deleteAsingleWorkout);
// Update a Workout
router.patch("/:id", workoutscontrollers.updateAworkout);
module.exports = router;
3. Workout Controller :
const express = require("express");
const mongoose = require("mongoose");
const router = express.Router();
const Workout = require("../models/workoutModel");
const getAllWorkouts = async (req, res) => {
// find() with or without {}
const allworkouts = await Workout.find();
res.status(200).json(allworkouts);
};
const getAsingleWorkout = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({ error: "There is no such Workout" });
}
const workout = await Workout.findById(id);
res.status(200).json(workout);
};
const postAnewWorkout = async (req, res) => {
const { title, reps, load } = req.body;
try {
const workout = await Workout.create({ title, reps, load });
res.status(200).json(workout);
} catch (error) {
res.status(404).json({ error: error.message });
}
}
const deleteAsingleWorkout = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({ error: "There is no such Workout" });
}
const workout = await Workout.findOneAndDelete({ _id: id });
res.status(200).json(workout);
}
const updateAworkout = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(404).json({ error: "There is no such Workout" });
}
const workout = await Workout.findOneAndUpdate({ _id: id }, {
...req.body
});
res.status(200).json(workout);
}
module.exports = {
getAllWorkouts,
getAsingleWorkout,
postAnewWorkout,
deleteAsingleWorkout,
updateAworkout
}
4. Workout Model :
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const workoutsSchema = new Schema({
title : {
type: String,
required:true
},
reps: {
type: Number,
required:true
},
load : {
type: Number,
required:true
}
}, {timestamps : true});
module.exports = mongoose.model("Workout", workoutsSchema);
Package.json (Backend)
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.2.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"g": "^2.0.1",
"mongoose": "^6.8.1",
"node": "^19.3.0",
"nodemon": "^2.0.20"
},
"description": ""
}
Now the Frontend
1. App.js (React) :
import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
function App() {
return (
<div className="App">
<BrowserRouter>
<Navbar />
<Routes>
<Route
path='/'
element={<Home/>}
/>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
2. Home.js
import { useState, useEffect } from "react";
import AddWorkout from "../components/AddWorkout";
const Home = () => {
const [workouts, setWorkouts] = useState(null);
useEffect(() => {
const fetchWorkouts = async () => {
const res = await fetch("http://localhost:5000/api/workouts");
const json = await res.json();
if (res.ok) {
setWorkouts(json);
}
};
fetchWorkouts();
}, []);
return (
<div className="home">
<div className="workouts">
{workouts &&
workouts.map((workout) => (
<div key={workout._id}>
<p>{workout.title}</p>
<p>{workout.reps}</p>
<p>{workout.load}</p>
</div>
))}
</div>
<div className="addForm">
<AddWorkout />
</div>
</div>
);
};
export default Home;
3. AddWorkout.js
import { useState } from 'react';
const AddWorkout = () => {
const [workout, setWorkout] = useState(null);
const [text, setText] = useState('');
const [reps, setReps] = useState(0);
const [load, setLoad] = useState(0);
const handleText = (e) => {
const text = e.target.value;
setText(text);
};
const handleReps = (e) => {
const reps = e.target.value;
setReps(reps);
};
const handleLoad = (e) => {
const load = e.target.value;
setLoad(load);
};
const handleForm = (e) => {
e.preventDefault();
const workout = {text, reps, load};
setWorkout(workout);
fetch("http://localhost:5000/api/workouts", {
method:'POST',
body:JSON.stringify(workout),
headers:{'content-Type': 'application/json'}
}).then(data => console.log(data))
.catch(err => console.log(err));
};
return (
<form onSubmit={handleForm}>
<label>Title</label>
<input type="text" value={text} onChange={handleText} />
<label>Reps</label>
<input type="text" value={reps} onChange={handleReps} />
<label>Load</label>
<input type="text" value={load} onChange={handleLoad} />
<input type="submit"/>
</form>
);
};
export default AddWorkout;
Here are some screenshots for more details :
and
So what am i doing wrong?
Thanks