I'm trying to create a rest api that would accept some simple data for an item. I used to use multer as file handler in my project. Here is my code for importing and handling the multer config:
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
And here is the corresponding app.post route for that:
// POST
router.post('/', upload.single('pi') ,(req, res, next) => {
console.log(req.body)
// console.log(req.file);
console.log('hi there')
const product = new Product({
// _id: new mongoose.Types.ObjectId(),
nm: req.body.name,
pr: req.body.price,
ca: req.body.category,
ti: req.body.title,
tg: req.body.tag
})
product
.save()
.then(result => {
res.status(201).json({
message: 'Handeling POST requests...',
createdProduct: {
_id: result._id,
nm: result.nm,
pr: result.pr,
ca: result.ca,
ti: result.ti,
tg: result.tg,
meta: {
type: 'get',
url: 'http://localhost:4000/products/' + result._id,
}
}
})
// console.log(result)
})
.catch(err => {
console.log(err)
res.status(500).json({
error: err
})
})
})
The Problem
The problem is that file is being uploaded to the uploads/ directory but the product is not being created. After the file is being uploaded to the corresponding directory I'm getting Could not get any response in Postman. Also I tried to log the req.body inside the multer but I did not get any thing in my terminal. I also set my Content-Type: to application/x-www-form-urlencoded and I'm using form-data in postman.
Related
I want to create an endpoint which should store file on server side as well as the some json data which should be received from same point is to be stored on mongodb.
I am using axios to send request from React App. Here is my Code.
const [companyFile, setCompanyFile] = useState(null);
const [company, setCompany] = useState({
name: "",
websiteUrl: "",
email: "",
companyLocation: "",
});
const AddCompany = async (e) => {
if (companyFile) {
e.preventDefault();
let formData = new FormData();
formData.append("company-file", companyFile);
formData.append("company", JSON.stringify(company));
axios({
method: "post",
url: `http://localhost:8080/company/add`,
data: formData,
withCredentials: true,
header: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
},
}).then((res) => console.log(res.data));
} else {
console.log("file not selected!!!!");
}
};
Now I don't know how to check if it is coming to backend express server or not? Or if coming then how to retrive application/json data from request for further process.
My question is that if data is sent to backend express then how to process that data in backend (i.e. get json data to create an document in mongodb).
Here is my code for company/add
let upload = multer({
storage: multer.diskStorage({
destination: async (req, file, cb) => {
if (!company) {
throw Error("Company cannot be found!");
}
let companyName = req.company.name;
let path = `./uploads/${companyName}/files`;
if (!fs.existsSync(path)) {
fs.mkdirSync(path, { recursive: true });
}
cb(null, path);
},
filename: async (req, file, cb) => {
// ** with student auth Code
req.filename = req.company.name;
cb(null, filename + path.extname(file.originalname));
},
}),
}).single("company-file");
// Add Company
module.exports.add_company = async (req, res) => {
try {
// Here I want to extract that company object to create new company
console.log(req.file);
try {
const newcompany = await Company.create(company);
req.company = newcompany;
upload(req, res, async () => {
try {
const companyFile = await CompanyFile.create({
companyId: req.company._id,
path: `./uploads/${req.company.name}/file/${req.filename}.pdf`,
});
req.companyFile = companyFile;
} catch (err) {
res.status(400).json({ success: false, message: err.message });
// ** code for resume-upload using student authentication middleware
if (
fs.existsSync(
`./uploads/${req.company.name}/file/${req.filename}.pdf`
)
) {
fs.unlink(`./uploads/${req.company.name}/file/${req.filename}.pdf`);
}
}
});
res.status(201).json({
success: true,
message: "Company Drive Added Successfully.",
company: req.company,
companyFile: req.companyFile,
});
} catch (err) {
res.status(400).json({
success: false,
errors: err,
message: "Error while applying company drive.",
});
}
} catch (err) {
console.log(err);
}
};
From the docs:
Multer adds a body object and a file or files object to the request
object. The body object contains the values of the text fields of the
form, the file or files object contains the files uploaded via the
form.
So in your case, you can access and parse the company field by doing:
const company = JSON.parse(req.body.company);
Also, you need to make sure to apply the multer middleware to your controller, e.g.
app.post('/company/add', upload.single('company-file'), function (req, res, next) {
// req.file is the `company-file` file
// req.body.company will hold the company text field
})
I'm learning Node.JS and as practise I need to create to endpoints:
GET /albums - Get a list of all albums in the dabatase
POST /purchases - Create a purchase
My attempt is as follows:
const mongoose = require('mongoose');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
// Imports
const Album = require("./models/album");
const Purchase = require("./models/purchase");
// TODO code the API
// Connect to DB
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true});
var conn = mongoose.connection;
conn.on('connected', function() {
console.log('database is connected successfully');
});
conn.on('disconnected',function(){
console.log('database is disconnected successfully');
})
conn.on('error', console.error.bind(console, 'connection error:'));
// Routes
app.get('/albums', function(req, res, next) {
Album.find({}, (err, albums) => {
if (!err) {
res.set({
'Content-Type': 'application/json',
'Status': 200,
})
return res.end(JSON.stringify(albums));
} else {
console.log('Failed to retrieve the Course List: ' + err);
}
});
});
// POST method route
app.post('/purchases', (req, res) => {
const purchase = new Purchase({
user: req.body.user,
album: req.body.album
})
purchase.save(function (err, post) {
if (err) { return err }
res.json(201, purchase);
})
})
module.exports = app;
Instructions for GET Request:
Since this is a JSON API, return JSON and a 200 status code, with the exception of destroy method which should return a 204 status code indicating no content.
All three Album columns title, performer and cost should be returned in a data object for the GET, POST and PUT methods. Here is an example of the format of response.body.data:
Expected Form:
response.body.data = {
_id: "the id of the album",
title: "Appetite for Destruction",
performer: "Guns N' Roses",
cost: 20
};
Instructions for POST Request:
The POST /purchases route should expect user and album properties to be set in the request body. It should then store a reference to both of these records on the newly created purchase record.
The response for POST /purchases should include the purchase record as well as the user and album relations, which should be populated with all their data fields.
Album Schema:
const albumSchema = mongoose.Schema({
performer: String,
title: String,
cost: Number
});
Purchase Schema:
const purchaseSchema = mongoose.Schema({
user: {type: mongoose.Schema.Types.ObjectId, ref: "User"},
album: {type: mongoose.Schema.Types.ObjectId, ref: "Album"}
})
The program need to pass the follwing two test cases for these endpoints:
describe("GET /albums", () => {
it("should return an array of all models", async () => {
const album = new Album(albumData).save();
const res = await chai
.request(app)
.get("/albums")
;
expect(res.status).to.equal(200);
expect(res).to.be.json;
expect(res.body.data).to.be.a("array");
expect(res.body.data.length).to.equal(1);
expect(res.body.data[0].title).to.equal(albumData.title);
expect(res.body.data[0].performer).to.equal(albumData.performer);
expect(res.body.data[0].cost).to.equal(albumData.cost);
}).timeout(2000);
});
describe("POST /purchases", () => {
it("should create a new purchase and return its relations", async () => {
const otherAlbumData = {
title: "Sample",
performer: "Unknown",
cost: 2,
};
const album = await new Album(otherAlbumData).save();
const user = await new User({name: "James"}).save();
const res = await chai
.request(app)
.post("/purchases")
.send({user, album})
;
expect(res.status).to.equal(200);
expect(res).to.be.json;
expect(res.body.data).to.haveOwnProperty("user");
expect(res.body.data.user).to.haveOwnProperty("name");
expect(res.body.data).to.haveOwnProperty("album");
expect(res.body.data.album).to.haveOwnProperty("title");
expect(res.body.data.user.name).to.equal(user.name);
expect(res.body.data.album.title).to.equal(album.title);
}).timeout(2000);
});
});
The problem is that GET /albums doesn't properly fetch the data. Error: "expected undefined to be an array" while POST /purchases throws error 500, "Cannot read property 'user' of undefined" but as per description "route should expect user and album properties to be set in the request body".
Can somebody gives me a headsup? I'm fairly new to Node.JS. Thanks.
you should add following code before Routes:
app.use(express.json({ limit: '15kb' }))
app.use(express.urlencoded({ extended: false }))
I setup multer like this
let multer = require('multer');
let apiRoutes = express.Router();
let UPLOAD_PATH = '../uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_PATH);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now());
}
});
let upload = multer({ storage: storage });
and in route I am getting data and an image and use multer like!
apiRoutes.post('/update', passport.authenticate('jwt', { session: false }), (request, response) => {
let record = {
name: request.body.name,
location: request.body.location,
about: request.body.about,
userid: request.body.userid,
avatar: request.body.filename
};
let userData = {
name: request.body.name
};
if (request.body.filename) {
upload(request, response, (error) => {
});
}
profile.findOneAndUpdate({ userid: request.body.userid }, record, {new: true}, (error, doc) => {
if (error) response.json(error);
user.findOneAndUpdate({ _id: request.body.userid }, record, (error, result) => {
if (error) throw error;
response.json(doc);
});
});
});
What is happening with this code is that when I do not send an image to backend then I get data from front end and store it into database. But when I send image along side data then it return POST /api/1.0/profile/update 401 0.396 ms - -.
It means I am not getting any data at all. Whats wring with the code here?
You can't use Multer in your /update route. Use Multer in your router like this:
var upload = multer({ dest: 'uploads/' })
apiRoutes.post('/profile', upload.single('image'), function (req, res, next) {
// Uploaaded
})
if you add it and still can't get our file, you should update your form with this parameter: enctype="multipart/form-data"
Sorry for this simple question. I'm just new to node.js and try to make a RESTapi with express. For file upload I'm using multer from npm. Everything is work fine. But when I try to access multer property in post method by using req.file.path it's throw this message:
{
"error": {
"message": "Cannot read property 'path' of undefined"
}
}
I also try to get other property available in multer like 'mimetype' but same error just property name changed. I'm using postman to send the data.
Here is my code snippets.
product.js
import express from 'express';
import mongoose from 'mongoose';
import Product from '../models/product.model';
import multer from 'multer';
const router = express.Router();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads');
},
filename: (req, file, cb) => {
cb(null, `${new Date().toISOString().replace(/:/g, '-')}${file.originalname}`);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, false);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), true);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
router.post('/', upload.single('productImage'), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
product.model.ts
import mongoose from 'mongoose';
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
price: {type: Number, required: true},
productImage: { type: String, required: true }
});
module.exports = mongoose.model('Product', productSchema);
I can't find the solution. I've check a couple of solution from stackoverflow but didn't find any solution.
if you use postman make sure you select "Content-Type" "multipart/form-data" in the headers and in the body make sure you select form-data and the key is "productImage" and type is file then value is the file you want upload
Sending file using cURL file name "image"
curl -X PUT http://www.example.com -F "image=#/file-path"
On server end get file name
upload(req, res, (err)=> {
if (err) {
console(err);
}
let filePath = req.files.image.file; // image is filename in cURL request
});
I'm new to node.js and try to built a simple REST Api with file uploading capabilities. For file upload I'm using multer from npm and using POSTMAN for sending post request. But when I try to upload file with multer I got this error:
{
"error": {
"message": "Cannot read property 'path' of undefined"
}
}
Here is my code for the API
product.js
import express from 'express';
import mongoose from 'mongoose';
import Product from '../models/product.model';
import multer from 'multer';
import multipart from 'connect-multiparty';
const router = express.Router();
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads');
},
filename: (req, file, cb) => {
cb(null, `${new Date().toISOString().replace(/:/g, '-')}${file.originalname}`);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, false);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), true);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
router.post('/', upload.single('productImage'), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
And here is the model
product.model.js
import mongoose from 'mongoose';
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {type: String, required: true},
price: {type: Number, required: true},
productImage: { type: String, required: false }
});
module.exports = mongoose.model('Product', productSchema);
Even I use connect-multipart middleware to fix the issue but then I got other error:
{
"error": {
"message": "stream ended unexpectedly"
}
}
Here is the code when use multipart
router.post('/', upload.single('productImage'), multipart(), (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
productImage: req.file.path
});
product.save().then(result => {
console.log(result);
res.status(201).json({
message: 'Created product successfully',
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: 'GET',
url: `http://localhost:3000/products/${result._id}`
}
}
});
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
I don't think it's a problem about POSTMAN.
Now can anyone help me to fix this. It's really a big problem for me to go ahead without solving the issue....
Thanks in advance.
I think the issue is that your call back is reversed. If the file is jpeg or png you will want (null,true). See below:
const fileFilter = (req, file, cb) => {
// reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(new Error('Only .jpeg or .png files are accepted'), false);
}
};