I have a react blog application with a form to submit a blog post. I have set up a server, routes, a model, and controllers for the fetch requests, and they all work when I use postman, but for some reason when I try to implement the post request on the submit button of the form, nothing gets sent to the database. Can someone help me figure out what I'm missing?
Here is the react code for the form
import React from 'react'
import fireIconImage from '../images/fireIcon.png'
import FireIcon from './FireIcon'
export default function BlogPostForm () {
const [formState, setFormState] = React.useState({ flaire: '', title: '', text: '', fireLevel: ''});
const [isHovered, setIsHovered] = React.useState();
const [isLit, setIsLit] = React.useState();
function changeFlaire(event) {
const selectedFlaire = event.target.value;
setFormState( {...formState, flaire: selectedFlaire });
}
function changeTitle(event) {
const title = event.target.value;
setFormState( {...formState, title: title });
}
function changeText(event) {
const text = event.target.value;
setFormState( {...formState, text: text });
}
function handleMouseOver(e) {
setIsHovered(e.target.id);
}
function handleMouseLeave(e) {
setIsHovered();
}
function handleFireIconClick(e) {
setIsLit(e.target.id);
}
function handleFireIconClass(fireLevel) {
const classNames = ['fireIcon']
classNames.push(`fireIcon${fireLevel}`)
if (isHovered >= fireLevel) {
classNames.push('isHeld')
}
if (isLit >= fireLevel) {
classNames.push('isLit')
}
return classNames.join(' ');
}
function submitForm(event) {
event.preventDefault();
const data = formState;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
fetch('http://localhost:8000/', options);
}
const fireIconsArray = [];
for (let i = 0; i < 5; i++) {
fireIconsArray.push(
<FireIcon
onClick={handleFireIconClick}
onMouseLeave={handleMouseLeave}
onMouseOver={handleMouseOver}
className={handleFireIconClass(i+1)}
src={fireIconImage}
alt="fire icon"
id={i+1}
key={i+1}
/>
)
}
return (
<form className="postForm">
<h1 className="postFormHeader">Create a blog post!</h1>
<select
required
className="flaireSelect"
value={formState.flaire}
onChange={changeFlaire}>
<option disabled={true} value="">Choose a flaire</option>
<option value="JavaScript">JavaScript</option>
<option value="CSS">CSS</option>
<option value="HTML">HTML</option>
<option value="REACT">REACT</option>
<option value="BACKEND">BACKEND</option>
</select>
<input
value={formState.title}
onChange={changeTitle}
className="titleBox"
placeholder="title"
type="text"
id="title"
name="title"
/>
<textarea
value={formState.text}
onChange={changeText}
className="textBox"
placeholder="text"
type="text"
id="blogPost"
name="blogPost"
/>
<div className="fireIconContainer">
{fireIconsArray}
</div>
<div className="blogPostFormButtonContainer">
<button className="blogPostSubmit" type="submit" onClick={submitForm}>SUBMIT</button>
<button className="blogPostCancel" type="submit">CANCEL</button>
</div>
</form>
)
}
Here is the controller code
const asyncHandler = require('express-async-handler')
const Post = require('../models/postModel')
//Set post
//route: POST /api/posts/id
//access: Private
const setPost = asyncHandler(async (req, res) => {
if (!req.body.title) {
res.status(400)
throw new Error('Please add a title')
}
const post = await Post.create({
title: req.body.title,
flaire: req.body.flaire,
postText: req.body.text,
fireLevel: req.body.fireLevel,
})
console.log(post)
res.status(200).json(post)
})
Here are the routes
const express = require('express')
const router = express.Router()
const {getPosts, setPost, updatePost, deletePost} = require('../controllers/postController')
//this tells which url route to use when a post and get request is made
router.route('/').get(getPosts).post(setPost)
//this tells which url route to use when a delete or put request is made
router.route('/:id').delete(deletePost).put(updatePost)
here is the server
const express = require ('express')
const dotenv = require('dotenv').config()
const port = 8000 //process.env.PORT//
const connectDB = require('./config/db')
const cors = require("cors")
const {errorHandler} = require('./middleware/errorMiddleware')
//connect the database to the server//
connectDB()
//initialize the app as express object
const app = express()
app.use(cors({
origin: "http://localhost:3000"
}))
//tell the app to accept incoming and outgoing req, res as json
app.use(express.json())
app.use(express.urlencoded({extended: false})) //?//
//not sure what this does, i think its telling the route it should look for//
app.use('/', require('./routes/postRoutes'))
//make sure the app utulizes the error handler functions//
app.use(errorHandler)
//tell the app what port to listen on
app.listen(port, () => console.log(`Server started on port ${port}`))
console.log('Hello World')
and here is the model
const mongoose = require('mongoose')
const postSchema = mongoose.Schema({
title: {
type: String,
required: [true, 'Please add a title']
},
flaire: {
type: String,
required: [true, 'Please select a flaire']
},
postText: {
type: String,
required: [true, 'Please add a body to your post']
},
fireLevel: {
type: Number,
required: [true, 'Please select a fire level']
}
},
{
timestamps: true,
}
)
module.exports = mongoose.model('Post', postSchema)
You forgot to set the fireLevel in your state so the form data was incomplete GOOFY
Related
I want to send book details along with the pdf of the book to the database.
This is a portion of my react code.
const {user}=useSelector((state)=>state.auth)
const [file, setFile] = useState(null);
const dispatch = useDispatch()
const { values, errors, touched, handleBlur, handleChange, handleSubmit, setFieldValue } = useFormik({
initialValues: initialValues,
validationSchema: addBookSchema,
onSubmit: async(values, { resetForm }) => {
// i have used FormData only to send pdf
const formData = new FormData()
formData.append('title', values.title)
formData.append('author', values.author)
formData.append('rating', values.rating)
formData.append('numOfRatings', values.numOfRatings)
formData.append('total', values.total)
formData.append('genre', values.genre)
formData.append('file', file)
const response = await axios.post('http://localhost:5000/api/admin', formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${user.token}`,
},
});
console.log(response.data);
resetForm()
}
})
return (
// some code
<Box>
<TextField
id="pdf-input"
// label="Upload PDF"
type="file"
accept=".pdf"
// className={classes.textField}
onChange={handleFileChange}
margin="normal"
/>
</Box>
// some code
Below is my express controller which handles this input from react
// this is to start connection
const url = process.env.MONGO_URI;
const connect = mongoose.createConnection(url, { useNewUrlParser: true, useUnifiedTopology: true });
let gfs;
connect.once('open', () => {
// initialize stream
gfs = new mongoose.mongo.GridFSBucket(connect.db, {
bucketName: "uploads"
});
});
// this is the controller
const addBook = asyncHandler(upload.single('file'),async (req, res) => {
console.log(req.file.file);
console.log(req.body);
// nothing gets logged in console
res.status(200).json(req.body)
});
below is my upload middleware used in my controller
const path = require("path");
const multer = require("multer");
const { GridFsStorage } = require("multer-gridfs-storage");
const crypto = require("crypto");
const storage = new GridFsStorage({
url: process.env.MONGO_URI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads",
};
resolve(fileInfo);
});
});
},
});
const upload = multer({ storage });
module.exports = upload;
i get the following errors
//1. if i do formData.append('file', file) then i get "Warning: An unhandled error was caught from submitForm()"
//2. if i do formData.append('ebook', file) then i get "message: 'Unexpected field', stack: 'MulterError: Unexpected field\n"
//3. Nothing gets logged in my console when i console.log(req.body) in my controller.
Note: I am able to successfully send data to my database if i do not use FormData and do not take file input.
I tried various methods but i could not identify the problem. Any helps is very much appreciated. Thanks in advance.
I was creating a book library management CRUD and I am Using React v18.
I am struggling to add data to my mongdb atlas because whenever I pass formData to axios.post via Redux, I'm always getting empty req.body on the backend controller.
This however does not occur when I am using postman when adding data to my database via form-data. My login form which also using axios.post works fine and the booklist using axios.get also works fine, the crud for book is the only one that is not working.
This is my bookCreate.js:
const BookCreate = () => {
const [title, setTitle] = useState('')
const [responsibility, setResponsibility] = useState('')
const [uniform_title, setUniform_title] = useState('')
const [parallel_title, setParallel_title] = useState('')
const [main_author, setMain_author] = useState('')
const [other_author, setOther_author] = useState('')
const [contributors, setContributors] = useState('')
const [corp_author, setCorp_author] = useState('')
const [placePub, setPlacePub] = useState('')
const [publisher, setPublisher] = useState('')
const [yearPub, setYearPub] = useState('')
...
const submitHandler = (e) => {
e.preventDefault();
const formData = new FormData();
formData.set('title', title);
formData.set('responsibility', responsibility);
formData.set('uniform_title', uniform_title);
formData.set('parallel_title', parallel_title);
formData.set('main_author', main_author);
formData.set('other_author', other_author);
formData.set('contributors', contributors);
formData.set('corp_author', corp_author);
formData.set('placePub', placePub);
formData.set('publisher', publisher);
formData.set('yearPub', yearPub);
dispatch(newBooks(formData))
}
return (
<Fragment>
<MetaData title={'TUP-T Online Library - Admin'} />
{/*<div className="row">*/}
<SideNavbarAdmin/>
<div></div>
<div className="dashboard-content">
<div className="dashboard-page">
<div className="dashboard-header">
<h1>Add Book</h1>
</div>
<div className="dashboard-body">
<form className="" onSubmit={submitHandler} encType='multipart/form-data'>
<div className="row g-3">
<div className="col md-6">
<div className="form-group">
<label htmlFor="title_field">Title</label>
<input
type="text"
id="title_field"
className="form-control"
name='title'
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
...
}
This is bookAction.js:
export const newBooks = (bookData) => async (dispatch) => {
try {
dispatch({ type: NEW_BOOK_REQUEST })
const config = {
headers: {
"Content-Type": "multipart/form-data"
// "Content-Type": "application/json"
}
}
// for(var pair of bookData.entries()) {
// console.log(pair[0]+ ', '+ pair[1]);
// }
const { data } = await axios.post('/api/v1/book/new', bookData, config)
dispatch({
type: NEW_BOOK_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: NEW_BOOK_FAIL,
payload: error.response.data.message
})
}
}
This is bookController.js:
exports.createBook = async (req, res, next) => {
console.log(req.body);
}
My app.js on the backend:
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
const errorMiddleware = require("./middlewares/errors");
app.use(express.json());
app.use(cookieParser());
app.use(errorMiddleware);
app.use(express.urlencoded({ extended: true }));
const book = require("./routes/book");
const auth = require("./routes/auth");
app.use("/api/v1", book);
app.use("/api/v1", auth);
app.use(errorMiddleware);
module.exports = app;
FormData send multipart/form-data, hence the parsers you used in app.js won't work. The common and easy way is to use multer. First install it:
npm install --save multer
Then where you hade urlencoded, and cookieParser use this:
const multer = require('multer');
const upload = multer();
app.use(upload.none())
I'm using this upload.none() because you seem to be not sending any file. If you are, you can visit the above link, mutter will let you do that.
Also you can simplify submitHandler since each form input has a name by getting ride of all those states and simply give the form to FormData() as parameter with e.target:
const submitHandler = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
dispatch(newBooks(formData));
};
I have created new route "/bookroom" for my application, however, I keep getting an error:
POST http://localhost:3000/api/bookings/bookroom 400 (Bad Request)
I am not sure what is causing this error. Below I have listed the code from my frotnend and backend. I would be gratefull for any help.
Server.js
const express = require("express");
const app = express();
const dbConfig = require('./db')
app.use(express.json())
const roomsRoute = require('./routes/roomsRoute')
const usersRoute = require('./routes/usersRoute')
const bookingsRoute = require('./routes/bookingsRoute')
app.use('/api/rooms', roomsRoute)
app.use('/api/users', usersRoute)
app.use('/api/bookings', bookingsRoute)
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`App listening on port ${port}`)
});
Backend - bookingsRoute.js
const express = require('express')
const router = express.Router()
const Booking = require("../models/booking")
router.post("/bookroom", async(req, res)=>{
const {
room,
userid,
fromdate,
todate,
totalamount,
totaldays} = req.body
try{
const newbooking = new Booking({
room: room.name,
roomid: room._id,
userid,
fromdate,
todate,
totalamount,
totaldays,
transactionId: '1234'
})
const booking = await newbooking.save()
res.send('Room booked successfully')
}catch(error){
return res.status(400).json({ message: 'error' });
}
})
module.exports = router
Fronted - Bookingscreeen.js
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import Loader from '../components/Loader'
import Error from '../components/Error'
import moment from 'moment'
function Bookingscreen({ match }) {
const [loading, setloading] = useState(true)
const [error, seterror] = useState()
const [room, setroom] = useState()
const roomid = match.params.roomid
const fromdate = moment(match.params.fromdate , 'DD-MM-YYYY')
const todate = moment(match.params.todate , 'DD-MM-YYYY')
const totaldays = moment.duration(todate.diff(fromdate)).asDays() + 1
const [totalamount, settotalamount] = useState()
useEffect(() => {
try {
setloading(true)
async function gettingRoom() {
const data = (await axios.post('/api/rooms/getroombyid', { roomid: match.params.roomid })).data
setroom(data)
setloading(false)
settotalamount(data.rentperday * totaldays)
}
gettingRoom()
}
catch (error) {
seterror(true)
console.log(error)
setloading(false)
}
}, [])
async function bookRoom(){
const bookingDetails = {
room,
userid: JSON.parse(localStorage.getItem('currentUser'))._id,
fromdate,
todate,
totalamount,
totaldays
}
try{
const result = await axios.post('/api/bookings/bookroom', bookingDetails)
}catch(error){
console.log(error)
}
}
return (
<div className='m-5'>
{loading ?
(<Loader />)
: room ?
(
<div>
<div className='row justify-content-center mt-6 bx_shadow'>
<div className='col-md-5'>
<h1>{room.name}</h1>
<img src={room.imageurls[0]} classname='fullsizeimg' />
</div>
<div className='col-md-5'>
<h1>Szczegóły rezerwacji</h1>
<hr></hr>
<b>
<p>Nazwa: {room.name} </p>
<p>Od: {match.params.fromdate}</p>
<p>Do: {match.params.todate}</p>
<p>Dni: {totaldays}</p>
<p>Cena za dzień: {room.rentperday} </p>
<p>Suma: {totalamount}</p>
</b>
<div>
<button style={{ float: 'right' }} className='room_btn' onClick={bookRoom}> Zarezerwuj</button>
</div>
</div>
</div>
</div>)
: (<Error />)
}
</div>
)
}
export default Bookingscreen
Model - bookings.js
const mongoose = require("mongoose")
const bookingSchema = mongoose.Schema({
room : {
type: String, required: true
},
roomid : {
type: String, required: true
},
userid : {
type: String, required: true
},
fromdate : {
type: String, required: true
},
todate : {
type: String, required: true
},
totalamount : {
type: Number, required: true
},
totaldays : {
type: Number, required: true
},
transactionid : {
type: String, required: true
},
status: {
type: String, required: true, default : "booked"
}
},{
timestamps: true,
})
const bookingmodel = mongoose.model('bookings', bookingSchema)
module.exports = bookingmodel
Your PORT ENV Is undefined currently so Your port is 5000. But you are requesting from localhost:3000
I'm a beginner in using states in reactjs so im sorry if my question shouldn't be asked in the right spot.Using React js, Node js and mongodb
So i created a constructor and initialized a variable called val inside it and passed that variable in a value property in a textarea tag so the user would enter somthing and it would be saved in the val variable (im not sure if thats the right way of doing it, so thats why im asking!). Also, I created a function called handleSubmit that would get the variable val and save it in mongodb, and i called that function in the button the user supposed to click when he passes in somthing. But all that doesn't seem to be working with me.
Here is my ping.js file:
class Pingbutton extends React.Component {
constructor(props) {
super(props);
this.state = { val: "Ford" };
}
handleSubmit = () => {
// if we get state error then we need to put it in a claa and call the constructot and then call the function using this.state.the name of the function
console.log("its running 1");
let databody = {
message: this.state.val,
};
console.log(" the message is :" + databody.message);
console.log(" the message is :" + this.state.val);
return fetch("http://localhost:5000/stored", {
method: "POST",
body: JSON.stringify(databody),
headers: {
"Content-Type": "application/json",
},
})
.then((res) => res.json())
.then((data) => console.log(data));
};
render() {
return (
<div className="ok2">
<textarea
className="message"
type="text"
placeholder="Write me somthing!. Also, double click to ping:) "
value={this.setState.val}
></textarea>
<button
className="button"
onClick={() => {
this.magic();
this.handleSubmit(); //animation + //pinging the phone
// this.handleButtonClick(); //setVal(() => ""); //sets the value of the box to empty
}}
>
</button>
</div>
);
}
}
export default Pingbutton;
and this is the back-end (nodejs) index.js file:
const express = require("express");
const cors = require("cors"); // Importing cors
var request = require("request");
const dotenv = require("dotenv");
const port = 5000;
var request = require("request");
var util = require("util");
const connectDB = require("./config/db");
require("dotenv").config({ path: "./config/config.env" });
const app = express();
dotenv.config();
connectDB();
app.use(cors({ origin: "http://localhost:3000" }));
app.get("/", (req, res) => {
res.send("Hey there!");
});
app.use(cors({ origin: "http://localhost:3000" }));
app.get("/Pinged", function (req, res) {
find_my_iphone("omar.fsm98#gmail.com", "Faresomar123", "Omar");
res.send("Pinged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
});
app.use(cors({ origin: "http://localhost:3000" }));
app.post("/stored", (req, res) => {
console.log("its running 2: " + req.body);
db.collection("quotes").insertOne(req.body, (err, data) => {
if (err) return console.log(err);
res.send("saved to db: " + data);
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
I have been getting Ford in the two console in the front-end after typing somthing in the box and clicking the button (excuting the handleSubmit) function). I tried doing this.state.val but didnt work then i changed it to this.setState.val and didnt work either.
I would appreciate the help.
THANK YOU!
The answer for that would be to create a function that does all that as following:
class App extends React.Component {
constructor() {
super();
this.state = {header: 'yeaheheh'};
}
changeHeader = (e) => {
e.preventDefault();
let newHeader = this.textInput.value;
console.log('submitted');
this.setState({header : newHeader});
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader} className="change-header-form">
<input id="input" ref={(input) => { this.textInput = input; }} type="text" placeholder="Enter Text Here" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('test'));
The full answer for that can be found in that link: Change a state in react
Trying to push register data to db but server returns error 500 (Internal Server Error), I am just training so dont know how to fix it here's my code. The browser that the problem is in query hook or route path but i cant find it
const express = require('express')
const config = require('config')
const mongoose = require('mongoose')
const app = express()
const PORT = config.get('port') || 5000
app.use(express.json({extended:true}))
app.use('/api/auth', require('./routes/auth.routes'))
async function start(){
try{
await mongoose.connect(config.get("mongoURL"),{
useNewUrlParser:true,
useUnifiedTopology:true,
useCreateIndex:true,
})
app.listen(PORT, () => console.log(`app has been started on port ${PORT}...`))
}catch(e){
console.log('server Error', e.message)
process.exit(1)
}
}
start()
this is my route (routes/auth.routes.js) Route should validate achieved data and save indo db. Validation working, but if i make real user it returns err 500
const {Router} = require('express')
const User = require('../models/User')
const bcrypt = require('bcryptjs')
const config = require('config')
const {check, validationResult} = require('express-validator')
const router = Router()
router.post(
'/register',
[
check('email', 'incorrect email').isEmail(),
check('password', 'min 6 symb')
.isLength({min:6})
],
async(req,res) => {
try{
const errors = validationResult(req)
if(!errors.isEmpty()){
return res.status(400).json({
errors:errors.array(),
message:'incorrect data'
})
}
const {email, password} = req.body
console.log(req)
const candidate = await User.findOne({email})
if(candidate){
return res.status(400).json({message:'Already exists'})
}
const hashedPassword = await bcrypt.hash(password, 12)
const user = new User({email, password: hashedPassword})
await user.save()
res.status(201).json({message:'created'})
} catch(e){
res.status(500).json({message:'something goes wrong try again'})
}
})
module.exports = router
config file maybe needs i dont know
{
"port":5000,
"jwtSecret":"secret",
"mongoUri":"mongourl/myFirstDatabase?retryWrites=true&w=majority"
}
hook that should push data to server
import {useCallback, useState} from 'react'
export const useHttp = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const request = useCallback( async(url,method = "GET", body=null, headers={}) => {
setLoading(true)
try{
if(body){
body = JSON.stringify(body)
headers['Content-Type'] = 'application/json'
}
const response = await fetch( url, {method, body,headers} )
const data = await response.json()
if(!response.ok){
throw new Error(data.message ||'something goes wrong')
}
setLoading(false)
return data
} catch (e) {
setLoading(false)
setError(e.message)
throw e
}
},[])
const clearError = useCallback(()=>setError(null),[])
return {loading, request, error, clearError}
}
and page where it all must works
import {useEffect, useState} from 'react'
import { useHttp } from '../hooks/http.hook'
import { useMessage } from '../hooks/message.hook'
const AuthPage = () => {
const {loading,error, request, clearError} = useHttp()
const [form, setForm] = useState({})
const message = useMessage()
const handleChange = (e) => {
setForm({...form, [e.target.name]:[e.target.value] })
}
useEffect(()=>{
message(error)
clearError()
}, [error])
const registerSubmit = async()=>{
try{
const data = await request('/api/auth/register', 'POST', {...form})
console.log('Data',data)
}catch(e) {
}
}
return(
<>
<div className="input-field ">
<input onChange={handleChange}name="email" placeholder="put email" id="email" type="text" />
<label htmlFor="email">Email</label>
</div>
<div className="input-field ">
<input onChange={handleChange} name="password"placeholder="put password" id="password" type="text" />
<label htmlFor="password">password</label>
</div>
<button onClick={registerSubmit} disabled={loading} className='btn black'>Register</button>
</>
)
}
Node server returns error
Error: Illegal arguments: object, number
[0] at _async (/Users/user/first-nodeapp/node_modules/bcryptjs/dist/bcrypt.js:214:46)
[0] at /Users/user/first-nodeapp/node_modules/bcryptjs/dist/bcrypt.js:223:17
[0] at new Promise (<anonymous>)
[0] at Object.bcrypt.hash (/Users/user/first-nodeapp/node_modules/bcryptjs/dist/bcrypt.js:222:20)
[0] at /Users/user/first-nodeapp/routes/auth.routes.js:33:45
[0] at processTicksAndRejections (internal/process/task_queues.js:93:5)