How to query MongoDB database using MongoDB, Express, React, and Node? - node.js

I cannot for the life of me figure out how to use a React, Express, Node, and MongoDB together to query the MongoDB database and display that information onto the DOM. My query works when hardcoded in, but when using the front-end text input, it does not work. Here is the code:
React Component:
const { facilities } = this.props.facility;
return(
<Container>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Input
type="text"
name="facilityState"
id="facilityState"
placeholder="Search State"
value={query}
onChange={this.onChange}
/>
</FormGroup>
<Button
color="dark"
style={{marginTop: '2rem'}}
block
> Search </Button>
</Form>
<Button onClick={this.checkReduxState}> Check Redux State </Button>
{ this.props.isAuthenticated ?
<ListGroup>
<TransitionGroup className="facilities-list">
{facilities.map(({_id, name, address, city, state, zip, phone, email}) => (
<CSSTransition key={_id} timeout={500} classNames="fade">
<ListGroupItem>
{ this.props.isAuthenticated ?
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={this.onDeleteClick.bind(this, _id)}
> ×</Button> : null }
<p> {name} </p>
<p> {address} </p>
<p> {city} </p>
<p> {state} </p>
<p> {zip} </p>
<p> {phone} </p>
<p> {email} </p>
</ListGroupItem>
</CSSTransition>
))}
</TransitionGroup>
</ListGroup> : null
}
</Container>
Express Route:
router.get('/query', (req, res) => {
Facility.find({
state: req.body.facilityState
// state: req.query.facilityState
})
.then(facilities => res.json(facilities));
});
Mongoose Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// mongoose.set('useCreateIndex', true);
const FacilitySchema = new Schema({
name: {
type: String,
required: true
},
address: {
type: String,
// text: true,
required: true
},
city: {
type: String,
required: true
},
state: {
type: String,
text: true,
index: true,
required: true
},
zip: {
type: Number,
required: true
},
phone: {
type: Number,
required: true
},
email: {
type: String,
required: true
},
});
// FacilitySchema.index({state: 'text'});
module.exports = Facility = mongoose.model('facility', FacilitySchema);
When I hard code the desired string value in for req.body.facilityState, pressing the submit button on the user interface works. Also, when using Postman this route works. But for some reason, the express req.body(or req.query) and the component aren't communicating with each other like they should. Can anyone help with this?
EDIT
Here is onChange function:
onChange = (e) => {
this.setState({ [e.target.name] : e.target.value })
}
EDIT 2
Here is redux action file query:
export const queryFacilities = () => dispatch => {
dispatch(setFacilitiesLoading());
axios
.get('/api/facilities/query')
.then(res =>
dispatch({
type: QUERY_FACILITIES,
payload: res.data
}))
.catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
}

I fixed the issue by changing my API call from
router.get('/query', (req, res) => {
Facility.find({
state: req.body.facilityState
// state: req.query.facilityState
})
.then(facilities => res.json(facilities));
});
to
router.get('/query/:state', (req, res) => {
Facility.find({
state: req.params.state
})
.then(facilities => res.json(facilities));
});
and also changing my Redux action from
export const queryFacilities = () => dispatch => {
dispatch(setFacilitiesLoading());
axios
.get('/api/facilities/query')
.then(res =>
dispatch({
type: QUERY_FACILITIES,
payload: res.data
}))
.catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
}
to
export const queryFacilities = (state) => (dispatch, getState) => {
dispatch(setFacilitiesLoading());
axios
.get(`/api/facilities/query/${state}`)
.then(res =>
dispatch({
type: QUERY_FACILITIES,
payload: res.data
}))
.catch(err => dispatch(returnErrors(err.response.data, err.response.status)));
}
So essentially I was able to pass my input through to the API by using req.params, not by using req.body.

Related

Getting an error while trying to read virtually populate array of comments

// model
import mongoose from "mongoose";
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: [true, "title is required"],
},
desc: {
type: String,
required: [true, "description is required"],
},
},
{ timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
);
postSchema.virtual("comments", {
ref: "Comment",
foreignField: "post",
localField: "_id",
});
const Post = mongoose.model("Post", postSchema);
export default Post;
// post controller
export const getPost = async (req, res) => {
try {
const post = await Post.findById(req.params.id).populate("comments");
res.status(200).json(post);
} catch (err) {
res.status(404).json(err);
}
};
//react code
import React, { useEffect, useState } from "react";
import axios from "axios";
const App = () => {
const [post, setPost] = useState({});
useEffect(() => {
const fetchData = async () => {
try {
const { data } = await axios.get("/api/posts/60d6463869eabd04241e18eb");
setPost(data);
console.log(data);
} catch (err) {
console.log(err);
}
};
fetchData();
}, []);
return (
<div>
<h3>hello</h3>
{/* <h1>
{post.comments.map((c) => (
<h2>{c.user.name}</h2>
))}
</h1> */}
<p style={{ color: "gray" }}>{post.desc}</p>
{post.comments.map((c) => (
<p key={c._id}>
<span style={{ fontWeight: "bold", textTransform: "uppercase" }}>
{c.user.name}:{" "}
</span>
{c.comment}
</p>
))}
</div>
);
};
export default App;
Hello good day, please i have an issue with this code, i'm able to read other fields except the comments field which i virtually populated it, could this be a problem with virtual populate or something else .
this is the error message:
Uncaught TypeError: post.comments is undefined
App App.js:26
React 17
js index.js:5
js main.chunk.js:282
Webpack 7
The above error occurred in the component:
App#http://localhost:3000/static/js/main.chunk.js:31:81
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

Cant upload Image with Reactjs Nodejs MongoDb

I'm trying to upload a image using React for my client side, Node Js as my server and monogoDb for my database. I'm using multer to store the images and everytime I make a request to create an account I get a 500 error. How do I make a post request that includes a Image to a monogoDb database from my React frontend through a Node Js server ?
Frontend
import React, { useState } from "react";
import {useHistory } from "react-router-dom";
import FormData from 'form-data'
import axios from "axios";
const CreateAccount = () => {
const api = "http://localhost:5000/user/create-account";
const history = useHistory();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [emailAddress, setEmailAddress] = useState("");
const [password, setPassword] = useState("");
const [gender, setGender] = useState("Male");
const [sexualPreference, setSexualPreference] = useState("Straight");
const [age, setAge] = useState("");
const [description, setDescription] = useState("");
const [picture, setPicture] = useState(null);
const [imgData, setImgData] = useState(null);
const account = {
firstName: firstName,
lastName: lastName,
emailAddress: emailAddress,
password: password,
gender: gender,
sexualPreference: sexualPreference,
age: age,
description: description,
pictures: picture
};
const onChangePicture = e => {
if (e.target.files[0]) {
console.log("picture: ", e.target.files);
setPicture(e.target.files[0]);
const reader = new FileReader();
reader.addEventListener("load", () => {
setImgData(reader.result);
});
reader.readAsDataURL(e.target.files[0]);
}
console.log(imgData);
console.log(picture);
};
const submit = () => {
let data = new FormData();
data.append('firstName', firstName);
data.append('lastName', lastName);
data.append('emailAddress', emailAddress);
data.append('password', password);
data.append('gender', gender);
data.append('sexualPreference',sexualPreference);
data.append('age', age);
data.append('description', description);
data.append('pictures',
{
uri: imgData,
name:`${picture}.jpg`,
type:'image/jpg'
});
axios.post(api, account, {
method: "post",
url: api,
data: account,
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
// handle success
console.log(res.data);
history.push({
pathname: "/",
});
})
.catch((error) => {
//handle error
console.log(account);
console.log(error);
});
}
const handleSubmit = (event) => {
event.preventDefault();
submit();
};
return (
<div>
<div>
<h1>Create account</h1>
</div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<p>First Name</p>
<input
id="firstName"
name="firstName"
type="firstName"
onChange={(e) => setFirstName(e.target.value)}
></input>
<p>Last Name</p>
<input
id="lastName"
name="lastName"
type="lastName"
onChange={(e) => setLastName(e.target.value)}
></input>
<p>Email Address</p>
<input
id="emailAddress"
name="emailAddress"
type="emailAddress"
onChange={(e) => setEmailAddress(e.target.value)}
></input>
<p>Password</p>
<input
id="password"
name="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
></input>
<p>Gender</p>
<select
id="gender"
name="gender"
type="gender"
onChange={(e) => setGender(e.target.value)}
>
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
<p>Sexual Preference</p>
<select
id="sexualPreference"
name="sexualPreference"
type="sexualPreference"
onChange={(e) => setSexualPreference(e.target.value)}
>
<option value="Straight" >Straight</option>
<option value="Gay" >Gay</option>
<option value="Lesbian" >Lesbian</option>
<option value="Bisexual" >Bisexual</option>
</select>
<p>Age</p>
<input
id="age"
name="age"
type="age"
onChange={(e) => setAge(e.target.value)}
></input>
<p>Description</p>
<input
id="description"
name="description"
type="description"
onChange={(e) => setDescription(e.target.value)}
></input>
<input
type="file"
name="file"
id="picture"
onChange={onChangePicture}
></input>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default CreateAccount;
Backend
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/');
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
})
const upload = multer({storage: storage})
router.post( "/user/create-account", upload.single("pictures"), [
check("firstName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "firstName"'),
check("lastName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "username"'),
check("emailAddress")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "emailAddress"'),
check("password")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "password"'),
check("gender")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "gender"'),
check("sexualPreference")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "sexualPreference"'),
check("age")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "age"'),
check("description")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "description"'),
check("pictures")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "pictures"'),
],
asyncHandler(async (req, res, next) => {
// Attempt to get the validation result from the Request object.
const errors = validationResult(req);
// If there are validation errors...
if (!errors.isEmpty()) {
// Use the Array `map()` method to get a list of error messages.
const errorMessages = errors.array().map((error) => error.msg);
// Return the validation errors to the client.
return res.status(400).json({ errors: errorMessages });
}
//new user request body using mongo model from schema
const postUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
emailAddress: req.body.emailAddress,
password: req.body.password,
gender: req.body.gender,
sexualPreference: req.body.sexualPreference,
age: req.body.age,
description: req.body.description,
pictures: req.file.path
});
console.log(req.file)
const userEmail = await User.findOne({
emailAddress: postUser.emailAddress,
});
if (postUser.emailAddress === userEmail) {
console.log("User with this email already exists");
return res.status(500).end();
} else if (postUser) {
//if true salts the password with bcryptjs
let salt = await bcryptjs.genSalt(10);
const hashPass = await bcryptjs.hash(postUser.password, salt);
console.log(hashPass);
postUser.password = hashPass;
postUser.save();
res.json({ postUser });
return res.status(201).end();
} else {
res.status(400).send({ error: "Error: Account not created" }).end();
}
})
);
schema
const mongoose = require('mongoose');
const userSchema = mongoose.Schema( {
firstName:{
type: String,
required: true
},
lastName: {
type: String,
require: true
},
emailAddress: {
type: String,
require: true
},
password:{
type: String,
required: true
},
gender:{
type: String,
required: true
},
sexualPreference: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
pictures: {
type: String,
required: true
},
matches: {
type: [{
Object
}],
},
})
module.exports = mongoose.model('User', userSchema);

User not existend in the database JWT?

So i have got a slight problem with the user authentication with JWT token. When i Login it returns to me "user doesnt exist", although there's clearly an user in the database. I've tried to fix by not looking for the password in the database and comparing it and it worked, but my question is how can i compare passwords in mongoose database and check if the user exists?
Login route:
//create route for sign in
router.post('/signin', async (req, res) => {
//destructure email and password from req.body
const user = await User.findOne({
email: req.body.email,
password: req.body.password,
})
if (!user) res.status(400).send({ message: 'User doesnt exist' })
else {
const valid = compare(req.body.password, user.password)
if (!valid) res.send(400).send({ message: 'Password doesnt match' })
if (valid) {
const accesstoken = createAccessToken(user._id)
const refreshtoken = createRefreshToken(user._id)
return res.send({
email: user.email,
refreshtoken: user.refreshtoken,
sendAccessToken: sendAccessToken(req, accesstoken),
sendRefreshToken: sendRefreshToken(refreshtoken),
})
}
}
})
user Model:
const mongoose = require('mongoose')
// const SALT_WORK_FACTOR = 10
// const bcrypt = require('bcryptjs')
//how a user will be stored in the mongodb schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true, index: { unique: true } },
email: { type: String, required: true, unique: true, dropDups: true },
password: { type: String, required: true, minlength: 6, trim: true },
isAdmin: { type: Boolean, required: true, default: false },
refreshtoken: { type: String },
})
const userModel = mongoose.model('Users', userSchema)
module.exports = userModel
login page:
import React, { useState, useEffect, useContext } from 'react'
import { userContext } from '../../App'
// import { useDispatch, useSelector } from 'react-redux'
// import { login } from '../../actions/userActions'
function Login(props) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useContext(userContext)
//acces the userLogin from redux store
// const userLogin = useSelector((state) => state.userLogin)
// const { loading, userInfo, error } = userLogin
// const dipsatch = useDispatch()
// useEffect(() => {
// if (userInfo) {
// props.history.push('/')
// }
// }, [userInfo])
//handlesubmit
const handleSubmit = async (e) => {
e.preventDefault()
const result = await (
await fetch('http://localhost:5000/users/signin', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
}),
})
).json()
if (result.accesstoken) {
setUser({
accesstoken: result.accesstoken,
})
window.location = '/'
} else {
alert(result.error)
}
}
useEffect(() => {
console.log(user)
}, [user])
return (
<div>
<h1 className="login-h">Login</h1>
<form onSubmit={handleSubmit} className="login">
<div className="login-form">
<label>email</label>
<br />
<input
type="email"
placeholder="username or email"
for="email"
onChange={(e) => setEmail(e.target.value)}
></input>
<br />
<label>Password</label>
<br />
<input
type="password"
placeholder="Password"
for="password"
onChange={(e) => setPassword(e.target.value)}
></input>
<br />
<p className="forget-pw">
Forgot password?
</p>
<button>Login</button> <br />
<br />
{/* <p style={{ color: 'red' }}>{result.error}</p> */}
</div>
<p className="have-ccl">
{' '}
No account yet?Signup
</p>
</form>
</div>
)
}
export default Login
There are a few problems:
Remove the unnecessary password: req.body.password line from your mongoose query. This is basically comparing unhashed passwords with hashed passwords in a plain JavaScript object. This never returns true because the hash is always different from the unhashed password.
Is it possible that your body is never actually sent? You need to add the name attribute to inputs before they can be parsed. Are you using body-parser?

Trying to add a product to list in NodeJS ("TypeError undefined")

I have just started NodeJS, so I'm on a beginner level. I am trying to build a shopping list app with MongoDB, in which user logs in, creates a list and then adds items to that list. I can register & log in and create a list, but when I try to add items to it, that's when I run into this:
"TypeError: Cannot read property 'products' of undefined".
Code for it is:
const user = req.user;
const shoppinglist_id = req.body.shoppinglist_id;
const name = req.body.name;
let new_product = product_model({
title: req.body.title,
imagePath: req.body.imagePath,
quantity: req.body.quantity
});
new_product.save().then(() => {
shoppinglist_id.products.push(new_product);
console.log('product saved');
shoppinglist_id.save().then(() => {
return res.redirect('/');
});
}); ```
User model:
const user_schema = new Schema({
name: {
type: String,
required: true
},
shoppinglists: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'shoppinglist',
req: true
}]
});
Shoppinglist model:
const shopping_list_schema = new Schema({
name: {
type: String,
required: true
},
products: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'product',
req: true
}]
});
Product model:
var schema = new Schema({
imagePath: {type: String, required: true},
title: {type: String, required: true},
quantity: {type: Number, required: true}
});
Any idea what I'm doing wrong? I know the problem is with this line:
"shoppinglist_id.products.push(new_product);", I have tried everything on it, "user.shoppinglists.products" etc. Nothing works. Any help?
EDIT:
I'll also post as a reference my add shoppinglist, which is working fine.
const user = req.user;
let new_shoppinglist = shopping_list_model({
name: req.body.name,
products: req.body.products
});
new_shoppinglist.save().then(() => {
console.log('shoppinglist saved');
user.shoppinglists.push(new_shoppinglist);
user.save().then(() => {
return res.redirect('/');
});
});
}```
EDIT #2:
I am adding how I am getting to shoppinglist-view:
´´´
const get_shoppinglist = (req, res, next) => {
const shoppinglist_id = req.params.id;
shopping_list_model.findOne({
_id: shoppinglist_id
}).then((shoppinglist) => {
shoppinglist.populate('products')
.execPopulate()
.then((shoppinglist) => {
let data = {
shoppinglist: shoppinglist
};
console.log(data);
let html = shoppinglist_views.shoppinglist_view(data);
res.send(html);
});
});
};´´´
And shoppinglist-view:
´´´
const shoppinglist_view = ((data) => {
let html = `
<html>
<body>
<h1></h1>
<h2>${data.shoppinglist.name}</h2>
<h4>Go back</h4>
<br>
`
data.shoppinglist.products.forEach((product) => {
html += products
html += `
<div>
<p>
<h2>Name of the shopping list: ${shoppinglists.shoppinglist.name}</h2>
<h3> Name: ${product.title}<br></h3>
<img src="${product.imagePath}" width="50px" height="50px" />
quantity: ${product.quantity} </p>
</div>
</body>
</html>
`;
});
html += `
<form action="/add-product" method="POST">
<p>Add products</p><br>
Title?<br>
<input type="text" name="title"><br>
Image-link<br>
<input type="img" name="imagePath"><br>
Quantity?<br>
<input type="number" name="quantity"><br><br>
<button type="submit">Add to list</button>
</form>
</html>
</body>`;
return html;
´´´
This is because shoppinglist_id is undefined. At the moment, you derive the value like this:
const shoppinglist_id = req.body.shoppinglist_id;
In other words, req.body has no property called shoppinglist_id. If you print the value of req.body, you will most likely see that it is a string. If you are attempting to send/receive JSON data (which I'm guessing you are) you must first parse the request body:
const data = JSON.parse(req.body);
const shoppinglist_id = data.shoppinglist_id;
Or even better:
const data = JSON.parse(req.body);
const { shoppinglist_id } = data;
Please note that you should always parse untrusted JSON inside a try ... catch block, but that's another lesson for another day.

How to make a submitted form undergo approval before appearing on the index page

Hi i'm building a app for people to be able to post jobs on a job board. But currently if a job is posted it goes straight to the homepage/index page and i don't want that. I want to be able to review the job posting before it goes live on the job board/homepage/index page. Any tutorial on this?
I.m currently using previous tutorial on Node to build this project.
// Example code, but similar to actual code
//models config
let jobSchema = new mongoose.Schema({
title: String,
category: String,
description: String,
type: String,
url: String,
email: String,
apply: String,
location: String,
company: String,
path: String,
created: {type: Date, default: Date.now}
})
let Job = mongoose.model('job', jobSchema);
// {type: String, default: "placeholdeimage.jpg"}
//routes config
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/');
},
filename: function (req, file, cb) {
var originalname = file.originalname;
var extension = originalname.split(".");
filename = Date.now() + '.' + extension[extension.length-1];
cb(null, filename);
}
});
app.get('/', (req, res) =>{
res.redirect('/jobs')
})
app.get('/jobs', (req, res) =>{
Job.find({}).sort({ date: -1}).find(function(err, jobs){
if(err){
console.log('error')
}
return res.render('index', {jobs: jobs})
})
})
//add form route
app.get('/jobs/add', (req, res) => {
res.render('add')
})
app.post('/jobs', multer({storage: storage, dest:
'./public/uploads/'}).single('file'), (req, res) => {
req.body.description = req.sanitize(req.body.description);
Job.create( (err, addJob) =>{
if(req.file){
let fullPath = "uploads/"+req.file.filename;
let document = {
title: req.body.title,
category: req.body.category,
description: req.body.description,
type: req.body.type,
url: req.body.url,
email: req.body.email,
apply: req.body.apply,
location: req.body.location,
company: req.body.company,
path: fullPath
};
let job = new Job(document);
job.save()
}else{
console.log('file not uploaded')
logo = 'noimage.jpg'
}
//redirect to index page
return res.redirect('/jobs')
})
})
app.get('/jobs/:id', (req, res) => {
Job.findById(req.params.id, (err, jobDetails) => {
if(err){
res.redirect('/jobs')
}else{
res.render('details', {job: jobDetails});
}
})
})
app.listen(port, process.env.PORT, process.env.IP, ()=> console.log(`Server
is running on ${port}`))
<div class="container">
<% for (const job of jobs) { %>
<div class="row">
<div class="column logo" style="width:10%;">
<img src='<%= job.path %>'>
</div>
<div class="column title">
<h2>
<%=job.title%>
</h2>
<p>
<%=job.company%>
</p>
</div>
<div class="column type">
<h2>
<%=job.type%>
</h2>
</div>
<div class="column location">
<h2>
<%=job.location%>
</h2>
</div>
<div class="column">
<h2><button>Apply</button></h2>
</div>
</div>
<hr>
<% } %>
</div>
You can add a field approved: { type: Boolean, default: false } to JobSchema and retrieve the jobs with a condition.
Job.find({approved:true}).sort({ date: -1})...

Resources