How to ignore "req.file.path" from form data if user do not choose a file using multer - reactjs

Hello I'm working on a social network project using MERN Stack and in there user can either make a post with only text or can make a post by uploading an image along with some text as a caption, but the problem is that when a user doesn't wish to upload image and just want to post only with text and leaves postImage field empty then this error occurs Cannot read property 'path' of undefined what can be the solution for this, I'm attaching the post schema, post routes and post state:
Post Schema:
const mongoose = require('mongoose');
const postSchema = mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: 'Users',
},
text: {
type: String,
required: [true, 'post cannot be empty'],
},
postImage: {
type: String,
},
name: {
type: String,
},
avatar: {
type: String,
},
likes: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
},
},
],
comments: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
},
comment: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Post', postSchema);
Post Route:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Post = require('../models/postModel');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads');
},
filename: function (req, file, cb) {
cb(null, file.originalname);
},
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === 'image/jpeg' ||
file.mimetype === 'image/png' ||
file.mimetype === 'image/gif'
) {
cb(null, true);
} else {
cb(new Error('The supported file types are jpeg, png and gif'), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
const { check, validationResult } = require('express-validator');
const User = require('../models/userModel');
router.post(
'/',
upload.single('postImage'),
[auth, check('text', 'Text is required').not().isEmpty()],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const user = await User.findById(req.user.id).select('-password');
const newPost = new Post({
text: req.body.text,
postImage: req.file.path,
name: user.name,
avatar: user.avatar,
user: req.user.id,
});
const post = await newPost.save();
res.json(post);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
Post State:
const createPost = async postData => {
try {
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
const res = await axios.post('/api/posts', postData, config);
dispatch({
type: ADD_POST,
payload: res.data,
});
} catch (err) {
dispatch({
type: POST_ERROR,
payload: err.response.msg,
});
}
};

You can simply check if req.file is defined - if yes set postImage to its path, else set it to undefined:
const newPost = new Post({
text: req.body.text,
postImage: req.file ? req.file.path : undefined,
name: user.name,
avatar: user.avatar,
user: req.user.id,
});

Related

Why is there a validation error for submitting this formik?

My problem is that I receive a validation error on my server when trying to update my user and product collection in mongodb using formik on my front end.
I add a new product as logged in user. All of the authentication is working with JWT token.
"ValidationError: Product validation failed: title: Path title is required., description: Path description is required., price: Path price is required., location: Path location is required., condition: Path condition is required., category: Path category is required."
I've looked everywhere on stack of, and tried everything for 30+ hours. If anyone could help, I'd appreciate it. I feel bad for asking this novice question, but I've been struggling.
Here is my front end code using formik and axios :
const schema = yup.object().shape({
title: yup.string().required('Title is required'),
description: yup.string().required('Description is required'),
price: yup.number().required('Price is required').positive('Price must be a positive number'),
location: yup.string().required('Location is required'),
condition: yup.string().required('Condition is required'),
category: yup.string().required('Category is required'),
});
const addProductFormik = useFormik({
initialValues: {
title: "",
description: "",
price: "",
location: "",
condition: "",
category: "",
},
validationSchema: schema,
onSubmit: async (values) =\> {
try {
const formData = new FormData();
formData.append('title', values.title);
formData.append('description', values.description);
formData.append('price', values.price);
formData.append('location', values.location);
formData.append('condition', values.condition);
formData.append('category', values.category);
console.log(formData.get('title'));
console.log(formData.get('price'));
const url = `http://localhost:3005/product/${user._id}/add-product`;
const config = {
headers: { Authorization: 'Bearer ' + token }
};
console.log(config);
const response = await axios.post(url, formData, config);
console.log(response);
const newProduct = response.data.product;
console.log(newProduct);
// dispatch(addProduct(newProduct));
} catch (error) {
console.log(error)
console.error(error);
}
},
});
Here is my controller function to update my mongo database :
export const createProduct = async (req, res, next) => {
try {
const id = req.params.userId;
const user = await User.findById(id);
if (!user) {
return res.status(404).json({ message: "User not found" });
}
console.log('user was found')
const createdProduct = new Product({
title: req.body.title,
description: req.body.description,
price: req.body.price,
location: req.body.location,
condition: req.body.condition,
category: req.body.category,
});
console.log(createdProduct);
console.log('product was created')
console.log(createdProduct._id);
try {
createdProduct.save();
user.products.push(createdProduct._id);
Product.insertMany(createdProduct);
// JWT token signing
const token = jwt.sign({ userId: user.id }, 'supersecretkey', { expiresIn: '1h' });
res.status(201).json({ product: createdProduct, token });
} catch (err) {
const error = new HttpError(
'Creating product failed, please try again.',
500
);
return next(error);
}
console.log('controller function works!');
} catch (error) {
console.error(error)
res.status(404).json({ message: error.message });
}
};
Here is my Product Schema :
import mongoose from "mongoose";
const Schema = mongoose.Schema;
const ProductSchema = new Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
location: {
type: String,
required: true,
},
condition: {
type: String,
required: true,
},
category: {
type: String,
required: true,
enum: ["Cars", "Electronics", "Clothing", "Furniture", "Other"],
},
seller: {
type: Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
type: Date,
default: Date.now,
},
});
const Product = mongoose.model("Product", ProductSchema);
export default Product;
Here is my User Schema :
import mongoose from "mongoose";
const Schema = mongoose.Schema;
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
min: 2,
max: 50,
},
lastName: {
type: String,
required: true,
min: 2,
max: 50,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
location: {
type: String,
required: true,
},
products: [
{
type: Schema.Types.ObjectId,
ref: "Product",
},
],
createdAt: {
type: Date,
default: Date.now,
},
});
const User = mongoose.model("User", UserSchema);
export default User
Now when I submit the form, there is no error on the front end. I get a 201 response and when I console log the newProduct in the addProductFormik, this is what it says :
_id: '63f27485ed59ed8c6fdff654', createdAt: '2023-02-19T19:12:05.981Z'}
createdAt: "2023-02-19T19:12:05.981Z"
_id: "63f27485ed59ed8c6fdff654"
On the back end, i get this error : "ValidationError: Product validation failed: title: Path title is required., description: Path description is required., price: Path price is required., location: Path location is required., condition: Path condition is required., category: Path category is required."
Now if you look at the console logs made in the controller function, these are the console logs that are logged on the server,
user was found
{
_id: new ObjectId("63f27485ed59ed8c6fdff654"),
createdAt: 2023-02-19T19:12:05.981Z
}
product was created
new ObjectId("63f27485ed59ed8c6fdff654")
controller function works!
So within my controller function, it finds the user in params, the createdProduct is only shown as a new ObjectId and not with all of its fields (title, description, price, etc). Somehow, it makes it through all the try blocks and console logs the controller function works. But my products collection and user collection (user.products) is not updated and I get that validation error.
Since you're using formData, you need to pass in the Content-Type through Axios headers when posting:
headers: { "Content-Type": "multipart/form-data" },
The default type is application/xml which won't work with your data.
You'll also need to make sure your backend is configured to handle that data coming in. body-parser is commonly used for this with Express.
Here's a simple configuration:
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json({ limit: '10mb' ))

Cannot read properties of undefined (reading 'findOne') in nextauth

Hi I building login form and doing authentication with using nextjs , nextauthjs and mysql
these are my codes .
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials"
import { query } from "../getdata";
import axios from "axios";
import { Users } from "../../../../server/models/Users";
import mysql from 'mysql2';
export default NextAuth({
session: {
strategy: 'jwt',
},
callbacks: {
async jwt({ token, user }) {
if (user?.id) token.id = user.id;
return token;
},
async session({ session, token }) {
if (token?.id) session.user.id = token.id;
return session;
},
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {
label: "username",
type: "text",
placeholder: "johndoe#test.com",
},
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
const user = await Users.findOne({ where: { username: credentials.username } });
console.log()
// database look up
if (
credentials.username === user.username &&
credentials.password === user.password
) {
return {
name: "John",
email: "johndoe#test.com",
};
} else {
throw new Error(errorMessage);
}
},
}),
],
});
I am getting this error when I am logging in
Cannot read properties of undefined (reading 'findOne')
Here are data for /models/Users i used this in next auth js.
once i used these code in express there its worked fine same thing i am doing in nextauth but it gives me error
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define("Users", {
username: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
role: {
type: DataTypes.STRING,
allowNull: false,
},
});
return Users;
};

Singe data from mongodb not showing

I am trying to get single products data from mongodb in reactjs but when I am mapping that data its not mapping and giving this error:
this is my backend way of getting single prod:
const getProdsById = asyncHandler(async (req, res) => {
const prod = await Prod.findById(req.params.id);
if (prod) {
res.json(prod);
} else {
res.status(404);
throw new Error("Product not found");
}
});
this is the routes page:
const {
getProds,
getProdsById,
postProds,
updateProds,
delProds,
} = require("../controllers/ProdControllers");
router.route("/").get(getProds).post(postProds);
router.route("/:id").delete(delProds).put(updateProds).get(getProdsById);
i tested this by using postman and it worked there easily but in the react part its not working and giving error!
[![enter image description here][2]][2]
product collection model:
const mongoose = require("mongoose");
const prodScheme = mongoose.Schema(
{
pname: {
type: String,
required: true,
},
longname: {
type: String,
required: true,
},
shortname: {
type: String,
required: true,
},
specs: {
type: Array,
required: true,
},
price: {
type: Number,
required: true,
},
discount: {
type: Number,
required: true,
},
netprice: {
type: Number,
required: true,
},
ratings: {
type: Number,
required: true,
},
category: {
type: Object,
required: true,
},
brand: {
type: Object,
required: true,
},
banner: {
type: String,
required: true,
},
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Prod", prodScheme);
my react frontend:
let [searchParams, setSearchParams] = useSearchParams();
const [Prods, setProds] = useState([]);
const getData = async () => {
try {
const presponse = await fetch(`/api/prods/${searchParams.get("id")}`);
const pjson = await presponse.json();
if (presponse.ok) {
setProds(pjson);
}
} catch (err) {
// console.error(err);
// alert(err.message);
}
};
useEffect(() => {
getData();
}, [Prods]);
{Prods.map((prod) => (
<h2>{prod.pname}</h2>
))}
btw searchParams is working fine i can see the exact id in url box aswell and also i printed it in console then also tested it with postman and it works fine

Can't get collection array from mongoDB with mongoose query

I have problem with getting data from DB. I want to get "collections" Array from mongoDB and render it in table component, but query returns null because of problem "user not found". Interesting thing is that if I use {email: req.body.email} in updateOne query to search for user and then create new collection it works and user is found.
getCollections.js
const router = require("express").Router();
const User = require("../models/user");
router.get("/", (req, res) => {
var query = { email: req.body.email };
User.find(query, (err, result) => {
if (err) {
res.json({ status: "error", error: "User not found" }, err);
} else {
res.json(result);
}
});
});
module.exports = router;
frontend getCollections query
useEffect(() => {
const url = "http://localhost:5000/api/getCollections";
// const url = `https://item-collection-app-bz.herokuapp.com/api/getCollections`;
axios
.get(url, { email: localStorage.getItem("email") })
.then((response) => {
setListOfCollections(response.data);
});
});
user.js UserSchema
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
username: { type: String, require: true },
password: { type: String, require: true },
email: { type: String, require: true },
admin: { type: Boolean },
blocked: { type: Boolean },
collections: [
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
coll_name: { type: String },
type: { type: String },
coll_desc: { type: String },
coll_image: { type: String },
items: [
{
_id: { type: mongoose.Schema.Types.ObjectId, required: true },
item_name: { type: String },
item_desc: { type: String },
comments: [
{
user: { type: String },
comment: { type: String },
comment_id: { type: String },
},
],
likes: { type: Number },
item_image: { type: String },
upload_date: { type: String },
},
],
},
],
});
userSchema.methods.generateAuthToken = function () {
const appToken = jwt.sign({ _id: this._id }, process.env.JWTPRIVATEKEY, {
expiresIn: "7d",
});
return appToken;
};
const User = mongoose.model("user", userSchema);
module.exports = User;
mongoDB
mongoDB structure
Tried User.findOne(), User.find()
SOLUTION
Thank you #Varun Kaklia. The solution is changing router.get and axios.get to router.post and axios.post.
Hey #1zboro1 first check did you receive any data from frontend inside Routes like-
const router = require("express").Router();
const User = require("../models/user");
router.get("/", (req, res) => {
const email = req.body.email;
console.log("Email from frontend", email)
var query = { email: req.body.email };
if (email != null || undefined) {
try {
const user = await User.find({ email});
console.log("User Details in User Routes of Backend",user)
if (user.length > 0) {
const currentUser = {
name: user[0].name,
email1: user[0].email1,
isAdmin: user[0].isAdmin,
_id: user[0]._id,
};
// console.log("Get User in User Routes of Backend", currentUser)
res.status(200).send(currentUser);
}
} catch (error) {
res.status(404).json({
message: "Something Went wrong",
});
}
Hope this code solve your query and give you desired result. If you still facing issue lemme know.
Thanks

Submitting two forms in React. One returns string while other returns json

I'm building one of my first admin areas for a site in React. I'm using mongoose and MongoDB. When I submit the form to add an artist to the db it does everything correctly and I can see it return the data as a JSON Object. When I submit a form that adds a piece of artwork to an artist it looks like it works but it actually does not add anything to the database as the data is sent as a string. I believe the only difference is one route is a POST and one is a Patch. I can't find any other difference. How can I make my form send data as a JSON instead of a string.
My Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const artistSchema = new Schema({
first_name: { type: String, required: true },
last_name: { type: String, required: true },
artist_painter: { type: Boolean, required: false},
artist_sculptor: { type: Boolean, required: false},
artist_other: { type: Boolean, required: false},
featured_image: { type: String, required: false},
artwork:[
{
type: { type: String, required: false},
title: { type: String, required: false},
width: { type: Number, required: false},
length: { type: Number, required: false},
depth: { type: Number, required: false},
image: { type: String, required: false},
media: { type: String, required: false},
price: { type: Number, required: false},
newRelease: { type: Boolean, required: false}
}
]
});
const Artist = mongoose.model("Artist", artistSchema);
module.exports = Artist
API Routes
// Matches with "/api/artists"
router.route("/")
.get(artistsController.findAll)
.post(artistsController.create);
// Matches with "/api/artists/:id"
router
.route("/:id")
.get(artistsController.findById)
.put(artistsController.update)
.delete(artistsController.remove);
// Matches with "/api/artists/art/:id"
router
.route("/art/:id")
.patch(artistsController.addArtById)
module.exports = router;
Artist Controller
const db = require("../models");
// Defining methods for the ArtistsController
module.exports = {
findAll: function(req, res) {
db.Artist
.find(req.query)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
findById: function(req, res) {
db.Artist
.findById(req.params.id)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
create: function(req, res) {
db.Artist
.create(req.body)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
update: function(req, res) {
db.Artist
.findOneAndUpdate({ _id: req.params.id }, req.body)
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
remove: function(req, res) {
db.Artist
.findById({ _id: req.params.id })
.then(dbModel => dbModel.remove())
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
addArtById: function(req, res) {
db.Artist
.findOneAndUpdate({ _id: req.params.id }, {$push: { artwork: req.body } } )
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
},
};

Resources