how to handle image upload in the mern stack using multer? - express

Im creating a MERN stack application and in the react front end, i intend to have a form to add a product, the form is going to have a lot of inputs including an image upload option. I want to know how to handle the image upload from the express side using Multer. i have used their documentation but im not sure whether the code i wrote is correct. I also haven't created the front end yet, so i am currently using postman to test the api. How do i test whether the image upload functionality is working using postman? I would be posting the code i have written so far for context.
Product model:
const mongoose = require('mongoose')
const ProductSchema = new mongoose.Schema({
name:{
type: String,
required: [true, 'please provide a product name'],
maxlength: 20,
minlength: 3
},
category: {
type: String,
required: [true, 'please provide a category'],
maxlength: 20,
minlength: 3
},
quantity: {
type: Number,
required: [true, 'please provide the quantity']
},
price: {
type: Number,
required: [true, 'please provide the price']
},
description: {
type: String,
required: [true, 'please provide the description'],
trim: true
},
image: {
type: String
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: [true, 'Please provide the user'],
}, },
{ timestamps: true } )
module.exports = mongoose.model('Product', ProductSchema)
file upload.js:
const multer = require('multer')
const { v4: uuidv4 } = require('uuid')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '../uploads')
},
filename: function(req, file, cb){
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname) )
}
})
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']
if(allowedTypes.includes(file.mimetype)) {
cb(null, true)
}else(
cb(null, false)
)
}
const upload = multer({storage, fileFilter})
module.exports = upload
Product router:
const express = require('express')
const router = express.Router()
const upload = require('../utils/fileUpload')
const {getAllProducts, createProduct, getProduct, updateProduct, deleteProduct} = require('../controllers/products')
router.route('/').post(upload.single('image'), createProduct).get(getAllProducts)
router.route('/:id').get(getProduct).patch(updateProduct).delete(deleteProduct)
module.exports = router
Product controller:
const Product = require('../models/Product')
const { StatusCodes } = require('http-status-codes')
const { BadRequestError, NotFoundError } = require('../errors')
const createProduct = async (req, res) => {
req.body.createdBy = req.user.userId
const product = await Product.create({...req.body, image: req.file})
res.send('create Product')
}
const getAllProducts = async (req, res) => {
res.send('get All products')
}
const getProduct = async (req, res) => {
res.send('get product')
}
const updateProduct = async (req, res) => {
res.send('update product')
}
const deleteProduct = async (req, res) => {
res.send('delete product')
}
module.exports = {
getAllProducts, createProduct, getProduct, updateProduct, deleteProduct
}

You can change the parameter type to file from postman to try uploading files when sending a request :

Related

Sequelize findAll() or findOne is not working

I apologize for something that could be very simple to fix, I just cannot figure it out. I've used this code in another project before with the exact same layout and that project works, but on this project, it's not working.
As seen below in the code, I am doing a simple findAll() query. The database is connected perfectly fine but I keep getting this error no matter what I try.
[Error is here][1]
I wanted to double-check to see if it's because of me or if it's an issue with the latest release of sequelize, but I doubt it because I haven't seen any issues like this pop up yet.
But if nobody can see an issue, I will submit a bug ticket.
root/routes/ShopItems.js
const express = require('express')
const router = express.Router()
const { ShopItems } = require('../models')
router.get('/', async(req, res) => {
const data = await ShopItems.findAll()
res.json(data)
})
module.exports = router
root/index.js
const express = require('express')
const session = require('express-session')
const app = express()
const cors = require('cors')
const db = require('./models')
const { SECRET, PORT } = require('./temp_secret')
app.use(express.json())
app.use(cors())
app.use(session({
secret: SECRET,
resave: true,
saveUninitialized: false,
}))
app.use(express.urlencoded({extended: false}))
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
req.header("Access-Control-Allow-Origin", "*")
req.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
next()
})
// Routers
const ShopItemsRouter = require('./routes/ShopItems')
app.use('/shop', ShopItemsRouter)
db.sequelize.sync().then(() => {
app.listen(PORT, () => {
console.log(`Running on ${PORT}`)
})
})
root/models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
root/models/ShopItems.js
module.exports = (sequelize, DataTypes) => {
const ShopItems = sequelize.define('shop_items', {
item_name: {
type: DataTypes.STRING(),
allowNull: false
},
item_description: {
type: DataTypes.STRING(),
allowNull: false
},
item_type: {
type: DataTypes.STRING(),
allowNull: false,
defaultValue: 'Single Item'
},
item_categories: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
item_genres: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
initial_item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_discount: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
number_of_sales: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
}
})
return ShopItems
}
Thank you anyone that helps, I greatly appreciate it.
[1]: https://i.stack.imgur.com/zBOTE.png
In the ShopItems routes file you have
const { ShopItems } = require('../models')
The problem with this is that the index.js file (Which is what gets brought in with the above statement), does not export a ShopItems property. This is why your error message is saying "Cannot read properties of undefined", because in this case the ShopItems variable is returning undefined.
Without seeing your ShopItems model I can't say for sure if this will work, but if that model exists and is set up properly, you should be able to do:
const db = require('../models')
And then inside of the route method:
const data = await db.ShopItems.findAll()

Getting Error 500 (general-error) while uploading files in feathersjs

I am getting this error on Postman when I send request for uploading files on Feathersjs:
{
"name": "GeneralError",
"message": "ENOENT: no such file or directory, open 'public/uploads/pic'",
"code": 500,
"className": "general-error",
"data": {},
"errors": {}
}
My uploads.service.js:
const {Uploads} = require('./uploads.class');
const createModel = require('../../models/uploads.model');
const hooks = require('./uploads.hooks');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (_req, _file, cb) => cb(null, 'public/uploads'), // where the files are being stored
filename: (_req, file, cb) => {
console.log(_req.body);
//cb(null, ${_req.body.name});
cb(null, `${_req.body.name}`); //
}, // getting the file name
});
const uploads = multer({
storage,
limits: {
fieldSize: 1e8,
fileSize: 1e7,
},
});
module.exports = function(app) {
const options = {
Model: createModel(app),
paginate: app.get('paginate'),
multi: true,
};
// Initialize our service with any options it requires
app.use(
'/uploads',
uploads.array('files'),
(req, _res, next) => {
const {method} = req;
if (method === 'POST' || method === 'PATCH') {
console.log(req.files);
console.log(req.body);
req.feathers.files = req.body.files;
const body = [];
for (const file of req.files)
body.push({
name: req.body.name,
newNameWithPath: file.path,
});
req.body = method === 'POST' ? body : body[0];
}
next();
},
new Uploads(options, app),
);
// Get our initialized service so that we can register hooks
const service = app.service('uploads');
service.hooks(hooks);
};
This is my uploads.model.js:
module.exports = function(app) {
const modelName = 'uploads';
const mongooseClient = app.get('mongooseClient');
const {Schema} = mongooseClient;
const schema = new Schema(
{
name: {type: String, required: true},
},
{
timestamps: true,
},
);
// This is necessary to avoid model compilation errors in watch mode
// see https://mongoosejs.com/docs/api/connection.html#connection_Connection-deleteModel
if (mongooseClient.modelNames().includes(modelName)) {
mongooseClient.deleteModel(modelName);
}
return mongooseClient.model(modelName, schema);
};
I really cannot figure out where exactly the problem is. According to me it is supposed to make the folder automatically when I upload the file.
I would really appreciate some help. Thank you in advance.
It was my own mistake. I made the uploads folder inside public folder myself and now it's working.

Request body is empty when submitting data using "form data"

When I update using raw JSON, it's working but when I use the form data it is not updating. the request body when using form data is an empty object. Why is this happening?
Here's my update code:
exports.updateProgram = catchAsync(async (req, res, next) => {
console.log('req ko body',req.body)
let doc = await Program.findByIdAndUpdate(req.params.id, req.body, { runValidators: true, new: true })
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(200).json({
status: 'success!',
data: { doc }
})
})
In Postman:
I am using multer, I actually pass the photo in req.body. Here's the code:
let multerStorage = multer.memoryStorage()
let multerFilter = (req, file, cb) => {
if (file.mimetype.split('/')[0] == 'image') {
cb(null, true)
} else {
cb(new AppError('Not an image!', 400), false)
}
}
let upload = multer({
storage: multerStorage,
fileFilter: multerFilter
})
exports.uploadPhotos = upload.fields([
{ name: 'abcd', maxCount: 10 },
{ name: 'photos', maxCount: 10 },
{name: 'photos3', maxCount: 10}
])
exports.resizePhotos = catchAsync(async (req, res, next) => {
// if (!req.files.photos || !req.files.abcd) return next()
if(req.files.abcd) {
req.body.abcd = []
await Promise.all(req.files.abcd.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.abcd.push(filename)
})
)} else if(req.files.photos3) {
req.body.photos3 = []
await Promise.all(req.files.photos3.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.photos3.push(filename)
})
)}
else if(req.files.photos) {
// console.log('codee here')
// } else if(req.body.photos) {
req.body.photos = []
console.log('req.files>>>', req.files)
await Promise.all(req.files.photos.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/programs/${filename}`)
req.body.photos.push(filename)
})
)
}
return next()
})
I'm importing in the routes file
Express (bodyParser) can't handle multipart form-data and that's why your code isn't working.
Take a look at multer, an express package. It is a middleware which provides the functionality you're looking for.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]);
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
This might be help you. Quoted from https://www.npmjs.com/package/multer#readme

How to link documents on mongoose

I am new to express development and i am trying to build a blog. I have built two models, one for posts and one for uses. On users schema i have an attribute posts to save the post when a user creates a post. On the controller, before i create a post first i am taking the user's id from the req.params and after that i retrive the user by findbyid function and try to save the post on user's posts attribute, but with no succeful.
const mongoose = require("mongoose");
UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}]
})
module.exports = mongoose.model("User", UserSchema);
const Post = require("../model/post");
const User = require("../model/user");
module.exports = {
new: (req, res) => {
res.render("new_post");
},
post_new: (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
})
User.findById(id)
.then(user => {
user.posts.push(post);
})
//post.created_by.push(id);
post.save()
.then(result => {
console.log("Post has created");
res.redirect("/");
});
}
};
I see a few problems.
You user schema should not have array of posts. Instead, your post schema should have a field named user/userId to store user ID.
Example:
const PostSchema = new mongoose.Schema({
title: { type: String },
....,
userId: {type: mongoose.Schema.Types.ObjectId, ref: "User"}
});
Now your post_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = await Post.create({
title: title,
article: article,
userId: id
});
console.log("Post has created");
res.redirect("/");
}
If you want to stick with your way then the create_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
});
const {_id} = await post.save();
const user = await User.findById(id);
user.posts.push(_id);
await user.save();
console.log("Post has created");
res.redirect("/");
}

express mongoose : getting aggregate returns empty array

Dears,
I'm having trouble in getting back mongoDB aggregation, using mongoose.
The query in MongoDB is sending back data, but the call from mongoose always returns empty array
UPDATE
I found the fix, I have to add a 3rd parameter when setting the model
const RawData = mongoose.model('rawdata', rawDataSchema, 'rawdata');
Here is the code
Schema rawdata.js
import mongoose from 'mongoose';
const rawDataSchema = new mongoose.Schema({
Email: String,
Nb: Number,
Licences: String,
Licence_ret: String,
Pays: String,
Share: String,
Pack: String,
Quota_in_MB: Number,
Webmail_Ext: Number, });
const RawData = mongoose.model('rawdata', rawDataSchema);
export default RawData;
index.js - edited after first feedbacks
import express from 'express';
import mongoose from 'mongoose';
import RawData from 'rawdata'
const app = express();
app.get('/test', async (req, res) => {
const data = await RawData.aggregate(
[
{ "$group": { _id: { license: "$Licence_Carrefour" }, count: { $sum: 1 } } },
{
$project: {
_id: 0,
License: "$_id.license",
count: 1
}
}
]);
console.log('data', data)
res.send('hello')
});
(async () => {
await mongoose.connect(dbURI, { useNewUrlParser: true }, err => {
if (err) console.log('\x1b[31m%s', 'DB is not connected');
console.log('connected to database ');
});
app.listen(APP_PORT, () => console.log('app up'));
})();