I'm having trouble with my controller Post to create a post.
I think the main problem is that the condition "if (req.file !== null)" is still working same if i'm not uploading a file (The picture field to be accurate because picture is supposed to be a file)
If someone already had this problem I would like to hear it ! Thank you by advance.
I will show you my code here :
post.controller.js (createPost only)
module.exports.createPost = async (req, res) => {
let fileName;
if (req.file !== null) {
try {
if (
req.file.mimetype != "image/jpg" &&
req.file.mimetype != "image/png" &&
req.file.mimetype != "image/jpeg"
)
throw Error("invalid file");
if (req.file.size > 500000) throw Error("max size");
} catch (err) {
const errors = uploadErrors(err);
return res.status(201).json({ errors });
}
fileName = req.body.posterId + Date.now() + ".jpg";
try {
await sharp(req.file.buffer)
.resize({ width: 150, height: 150 })
.toFile(`${__dirname}/../client/public/uploads/posts/${fileName}`
);
} catch (err) {
res.status(400).send(err);
}
}
const newPost = new postModel({
posterId: req.body.posterId,
message: req.body.message,
picture: req.file !== null ? "./uploads/posts/" + fileName : "",
video: req.body.video,
likers: [],
comments: [],
});
try {
const post = await newPost.save();
return res.status(201).json(post);
} catch (err) {
return res.status(400).send(err);
}
};
post.routes.js :
const router = require('express').Router();
const postController = require('../controllers/post.controller');
const multer = require("multer");
const upload = multer();
router.get('/', postController.readPost);
router.post('/', upload.single("file"), postController.createPost);
postModel :
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
posterId: {
type: String,
required: true
},
message: {
type: String,
trim: true,
maxlength: 500,
},
picture: {
type: String,
},
video: {
type: String,
},
likers: {
type: [String],
// required: true,
},
comments: {
type: [
{
commenterId:String,
commenterPseudo: String,
text: String,
timestamp: Number,
}
],
// required: true,
},
},
{
timestamps: true,
}
);
module.exports = mongoose.model('post', PostSchema);
It's because when you don't upload image, the req.file will be undefined, and NOT null.
Try to change your code from this:
if (req.file !== null) {
to this:
if (req.file) {
Related
i was following brad traversy's one of his udemy course. after working on adding experience in profile in profile routes. i all time get server error. but it should end up with full profile details like in brad course. this is my github link for that project
https://github.com/arshad007hossain/findDevs
profile.js
const express = require("express");
const router = express.Router();
const auth = require("../../middleware/authmiddleware");
const { check, validationResult } = require("express-validator");
const Profile = require("../../models/Profile");
const User = require("../../models/User");
// #route GET api/profile/me
// #desc Get current users profile
// #access Private
router.get("/me", auth, async (req, res) => {
try {
const profile = await Profile.findOne({
user: req.user.id,
}).populate("user", ["name", "avatar"]);
if (!profile) {
return res.status(400).json({ msg: "There is no profile for this user" });
}
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
// #route POST api/profile/me
// #desc create or update users profile
// #access Private
router.post(
"/",
[
auth,
[
check("status", "status is required").not().isEmpty(),
check("skills", "skills is required").not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubusername,
facebook,
linkedin,
twitter,
instagram,
youtube,
skills,
} = req.body;
//build user profile
const profileFields = {};
profileFields.user = req.user.id;
if (company) profileFields.company = company;
if (website) profileFields.website = website;
if (location) profileFields.location = location;
if (bio) profileFields.bio = bio;
if (status) profileFields.status = company;
if (githubusername) profileFields.githubusername = githubusername;
if (skills) {
profileFields.skills = skills.split(",").map((skill) => skill.trim());
}
//build social objects
profileFields.social = {};
if (youtube) profileFields.social.youtube = youtube;
if (twitter) profileFields.social.twitter = twitter;
if (linkedin) profileFields.social.linkedin = linkedin;
if (instagram) profileFields.social.instagram = instagram;
if (facebook) profileFields.social.facebook = facebook;
//console.log(profileFields.skills);
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//Update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
);
return res.json(profile);
}
//create
profile = new Profile(profileFields);
await profile.save();
res.json(profile);
} catch (err) {
console.errora(err.message);
res.status(500).json("server error");
}
}
);
// #route GET api/profile
// #desc Get all profile
// #access Public
router.get("/", async (req, res) => {
try {
let profiles = await Profile.find().populate("user", ["name", "avatar"]);
res.json(profiles);
} catch (err) {
console.error(err.message);
res.status(500).json("server error");
}
});
// #route GET api/profile/user/user_id
// #desc Get single profile
// #access Public
router.get("/user/:user_id", async (req, res) => {
try {
const profile = await Profile.findOne({
user: req.params.user_id,
}).populate("user", ["name", "avatar"]);
if (!profile) return res.status(400).json({ msg: "Profile not found" });
res.json(profile);
} catch (err) {
if (err.kind == "ObjectId") {
return res.status(400).json({ msg: "Profile not found" });
}
console.error(err.message);
res.status(500).json("server error");
}
});
// #route DELETE api/profile
// #desc Delete profile, user
// #access Private
router.delete("/", auth, async (req, res) => {
try {
// Remove profile
await Profile.findOneAndRemove({ user: req.user.id });
// Remove user
await User.findOneAndRemove({ _id: req.user.id });
res.json({ msg: "User deleted" });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
// #route PUT api/profile/experience
// #desc Add profile experience
// #access Private
router.put(
'/experience',
[
auth,
[
check('title', 'Title is required field').not().isEmpty(),
check('company', 'Company is required field').not().isEmpty(),
check('from', 'From date is required field').not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
title,
company,
location,
from,
to,
current,
description,
} = req.body;
const newExp = {
title,
company,
location,
from,
to,
current,
description,
};
try {
const profile = await Profile.findOne({ user: req.user.id });
profile.experience.unshift(newExp);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
module.exports = router;
User.js
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = User = mongoose.model("user", UserSchema);
Profile.js Model
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('profile', ProfileSchema);
I've two endpoints: one that delete product document and one that retrieve the document.
After I delete the document throught by Id, the GET api call return me already the document even if it's deleted and It's not present on MongoDb.
Response of DELETE call returns { deletedCount: 1 }
Here code of GET product:
exports.getSingleProduct = (req, res, next) => {
let id = req.params.id;
Products.findById(id).populate({ path: 'internalColor' }).then(result => {
if(result && result.visible == true) {
res.status(200).json(result)
} else {
res.status(404).json({
message: 'product_not_found',
id: id
})
}
}).catch(err => {
res.status(404).json({
message: 'error_operation: ' + err,
id: id
})
});
}
Here code of DELETE product:
exports.deleteDefinallySingleProduct = (req, res, next) => {
let id = req.params.id;
console.log(id)
Products.deleteOne({ id: id }).then(result => {
if(result) {
res.status(200).json({
message: 'deleted_product'
});
}
}).catch(err => {
res.status(404).json({
message: 'error_operation: ' + err
})
})
}
Products Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const options = {
timestamps: true
}
const productSchema = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
externalUrl: {
type: String,
required: true
},
imgUrl: {
type: String,
required: true
},
brand: {
type: String,
required: true
},
visible: {
type: Boolean,
required: true
}
}, options);
const Product = mongoose.model('Products', productSchema);
module.exports = Product;
I think the error that you are facing is caused by a typo in your code.
exports.deleteDefinallySingleProduct = (req, res, next) => {
...
Products.deleteOne({ id: id }).then(result => {
if(result) {
// this code will run always
console.log(result) // will show { n: 0, ok: 1, deletedCount: 0 },
// That is, this section of code will run always despite of delete count being 0 or more due to the request to be excecuted successfully.
...
}
The correct implementation is here:
exports.deleteDefinallySingleProduct = (req, res, next) => {
...
Products.deleteOne({ _id: id }).then(result => {
...
}
This is because by default mongooose use _id representing the document id, unless create a custom id in the schema which you didn't do.
I am creating the backend of an ecommerce store and I have to validate the photos. I started with an idea, but my teacher has made me change and now I don't know how to combine what I have done with what he asks of me. Let me explain the situation: I have created a special path for uploading the image to cloudinary. This is the code:
const router = require('express').Router()
const cloudinary = require('cloudinary')
const auth = require('./../middleware/auth')
const authAdmin = require('./../middleware/authAdmin')
const fs = require('fs-extra')
// we will upload image on cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.CLOUD_API_KEY,
api_secret: process.env.CLOUD_API_SECRET
})
// Upload image only admin can use
router.post('/upload', auth, authAdmin, (req, res) =>{
try {
if(!req.files || Object.keys(req.files).length === 0)
return res.status(400).json({msg: 'No files were uploaded.'})
const file = req.files.file;
if(file.size > 1024*1024) {
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "Size too large"})
}
if(file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/png'){
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "File format is incorrect."})
}
cloudinary.v2.uploader.upload(file.tempFilePath, {folder: "ecommerce"}, async(err, result)=>{
if(err) throw err;
removeTmp(file.tempFilePath)
res.json({public_id: result.public_id, url: result.secure_url})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
// Delete image only admin can use
router.post('/destroy',auth , authAdmin, (req, res) =>{
try {
const {public_id} = req.body;
if(!public_id) return res.status(400).json({msg: 'No images Selected'})
cloudinary.v2.uploader.destroy(public_id, async(err, result) =>{
if(err) throw err;
res.json({msg: "Deleted Image"})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
const removeTmp = (path) =>{
fs.unlink(path, err=>{
if(err) throw err;
})
}
module.exports = router
I have a product model made like this:
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name:{
type: String,
trim: true,
required: true
},
price:{
type: Number,
trim: true,
required: true
},
description:{
type: String,
required: true
},
images:{
type: Object,
required: true
},
category:{
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
}, {
timestamps: true
})
module.exports = mongoose.model("Products", productSchema)
And this is my function to create the product:
createProduct: async (req, res) => {
try {
const {
name,
price,
images,
description,
categoryId
} = req.body;
if (!images) return res.status(400).json({
message: "No hay imagen del producto!"
})
if (!(name || price || description))
return res.status(400).json({
message: "Por favor, complete todos los campos"
})
const product = await Products.findOne({
name
})
if (product)
return res.status(400).json({
message: "Este producto ya ha sido creado anteriormente"
})
const newProduct = new Products({
name: name.toLowerCase(),
price,
description,
categoryId,
images
})
await newProduct.save()
res.json({
message: "Producto creado ! "
})
} catch (err) {
return res.status(500).json({
message: err.message
})
}
},
And this is the route for create product :
app.use('/api', require('./routes/productRouter'))
router.post('/products', auth, authAdmin, productCtrl.createProduct)
The point is that, first, I would upload the image, and when creating the product, it would pass through Postman the results of the image upload (public_id and url).
In the review, my teacher told me to put everything together in the same route, that of creating the product, I have been trying all morning and there is no way it will work for me. Can somebody help me ? I don't know exactly what the function should look like with the integrated image.
Kind regards, thank you in advance
// Create Product -- Admin
exports.createProduct = catchAsyncErrors(async (req, res, next) => {
let images = [];
if (typeof req.body.images === "string") {
images.push(req.body.images);
} else {
images = req.body.images;
}
const imagesLinks = [];
for (let i = 0; i < images.length; i++) {
const result = await cloudinary.v2.uploader.upload(images[i], {
folder: "products",
});
imagesLinks.push({
public_id: result.public_id,
url: result.secure_url,
});
}
req.body.images = imagesLinks;
req.body.user = req.user.id;
const product = await Product.create(req.body);
res.status(201).json({
success: true,
product,
});
});
I'm trying to update a transaction, but instead its updating that one and at the same time deleting the other ones. My focus is to just update one transaction. Can someone check my backend logic please.
My Schema:
const mongoose = require('mongoose')
mongoose.Schema.Types.String.set('trim', true)
const transactionSchema = mongoose.Schema(
{
note: { type: String, required: true },
amount: { type: Number, default: 0 },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
},
{
timestamps: true,
}
)
const WalletSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
name: {
type: String,
trim: true,
required: [true, 'Please enter a name'],
},
balance: {
type: Number,
default: 0,
},
transactions: [transactionSchema],
createdAt: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('Wallet', WalletSchema)
My backend controller logic:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body
const foundWallet = await Wallet.findOne({ user: req.user.id })
foundWallet.transactions = foundWallet.transactions.filter(
(trans) => trans._id.toString() === req.params.id
)
if (foundWallet.transactions) {
foundWallet.transactions[0].amount =
amount || foundWallet.transactions[0].amount
foundWallet.transactions[0].note = note || foundWallet.transactions[0]
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
})
The problem is that you're using the filter method of Array that will only return the specified transaction.
the better approach is to write your code using the map method like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
if (!transIndex) return next(new ErrorResponse("Transaction not found", 404));
foundWallet.transactions = foundWallet.transactions.map((trans) => {
if (trans._id.toString() === req.params.id) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
}
return trans;
});
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
or you can do it like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
const trans = foundWallet.transactions[transIndex];
if (trans) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
} else {
return next(new ErrorResponse("Transaction not found", 404));
}
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
You are overwriting the transaction. Better to iterate over each transaction and update on matching transaction id.
exports.updateWalletTransactions = asyncHandler(async(req, res, next) => {
const {
amount,
note
} = req.body;
const foundWallet = await Wallet.findOne({
user: req.user.id
})
let transFound = false;
if (foundWallet) {
foundWallet.transactions.forEach(trans => {
if (trans._id.toString() === req.params.id) {
transFound = true;
trans.amount = amount || trans.amount
trans.note = note || trans.note
}
})
if(transFound){
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
} else {
return next(new ErrorResponse('User Id not found', 404))
}
})
I have a "Drinkers" model and a "Sodas" model which is "related" - a drinker can have drunk X amount of sodas.
The route to get the data is this
router.get('/all/:drinkerId', sodasController.getAllSodasFromDrinker)
In my sodasController, is there a way to check if :drinkerId exists in the "Drinkers" collection and if not return an error that the drinker doesn't exist, without having to require the drinkersController in the sodasController.
Right now getAllSodasFromDrinker looks like this
const Sodas = require("../models/sodas.model");
exports.getAllSodasFromDrinker = async (req, res, next) => {
try {
const id = req.params.drinkerId;
if (id.match(/^[0-9a-fA-F]{24}$/)) {
await Sodas.find({ drinker: id }).exec((err, drinkerItem) => {
if (err) {
return next(err);
}
res.json({ data: drinkerItem });
});
} else {
return next("ID is in the wrong format");
}
} catch (error) {
return next(error);
}
};
In that function, I want to check if a user exists with the applied ID.
I want to avoid having to
const Drinkers = require("../models/drinkers.model") in the sodasController
The Drinkers model:
const Schema = mongoose.Schema;
const drinkersSchema = new Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
sodas: {
type: Schema.Types.ObjectId,
ref: "Sodas",
},
},
{ timestamps: true }
);
The Sodas model
const Schema = mongoose.Schema;
const sodaSchema = new Schema(
{
name: {
type: String,
required: true,
},
drinker: {
type: Schema.Types.ObjectId,
ref: "Drinkers",
},
},
{ timestamps: true }
);
I would add a middleware function which validates if the drinkerId exists. If it exists, you can continue to the controller. If not, then you should throw a 404 error.
Your route:
router.get(
'/all/:drinkerId',
drinkerMiddleware.exists,
sodasController.getAllSodasFromDrinker
);
drinkerMiddleware:
exports.exists = async (req, res, next) => {
try {
const drinker await Drinker.find({ drinker: req.params.drinkerId }).exec();
if (!drinker) {
return next("Drinker not found.");
}
return next();
} catch (error) {
return next(error);
}
};