For your middleware to work on your routes, you will need to modify them slightly, as a request containing a file from the front end will have a different format.
First, add your multer middleware to your POST route in your stuff router:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const multer = require('../middleware/multer-config');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllStuff);
router.post('/', auth, multer, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;You now need to update your controller to correctly handle the new incoming request:
exports.createThing = (req, res, next) => {
req.body.thing = JSON.parse(req.body.thing);
const url = req.protocol + '://' + req.get('host');
const thing = new Thing({
title: req.body.thing.title,
description: req.body.thing.description,
imageUrl: url + '/images/' + req.file.filename,
price: req.body.thing.price,
userId: req.body.thing.userId
});
thing.save().then(
() => {
res.status(201).json({
message: 'Post saved successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};So what are we doing here?
To add a file to the request, the front end needed to send the request data as form-data as opposed to JSON. The request body contains a thing string, which is simply a stringified thing object. Therefore, you need to parse it using JSON.parse() to get a usable object.
You need to resolve the full URL because your image req.file.filename only contains the filename segment. Use req.protocol to get the first segment ( 'http' , in this case). Add the '://' , and then use req.get('host') to resolve the server host ('localhost:3000' ). Finally, add '/images/' and the filename to complete your URL.
If you save the controller as is and try out the app (remember to use the part 4 section!), you will see that almost everything works. You only get a 404 error when trying to fetch the image, even though it looks like your URL is correct. So what's happening here?
Well, you are making a GET request to http://localhost:3000/images/<image-name>.jpg . It seems simple, but remember that the Express app is running on localhost:3000 , and you haven't told it how to react to requests going to that endpoint, so it is returning a 404 error. Therefore, you need to tell your app.js how to handle those requests, serving up your static images folder.
You will need a new import in app.js :
const path = require('path');Add the following route handler just above your current routes:
app.use('/images', express.static(path.join(__dirname, 'images')));This tells Express to serve up the static resource images (a sub-directory of your base directory, __dirname ) whenever it receives a request to the /images endpoint. Save and refresh the app in the browser: things should now be working properly! Now on to the PUT route!
Modifying the PUT route is slightly more complicated, as you have to take into account both possibilities: the user has updated the image, or they have not. In the first case, you will receive form-data and file; in the second case, you will just receive JSON data.
First, add multer as middleware to your PUT route:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const multer = require('../middleware/multer-config');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllStuff);
router.post('/', auth, multer, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, multer, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;Now modify your modifyThing() function to check if you have received a new file, and react accordingly:
exports.modifyThing = (req, res, next) => {
let thing = new Thing({ _id: req.params._id });
if (req.file) {
const url = req.protocol + '://' + req.get('host');
req.body.thing = JSON.parse(req.body.thing);
thing = {
_id: req.params.id,
title: req.body.thing.title,
description: req.body.thing.description,
imageUrl: url + '/images/' + req.file.filename,
price: req.body.thing.price,
userId: req.body.thing.userId
};
} else {
thing = {
_id: req.params.id,
title: req.body.title,
description: req.body.description,
imageUrl: req.body.imageUrl,
price: req.body.price,
userId: req.body.userId
};
}
Thing.updateOne({_id: req.params.id}, thing).then(
() => {
res.status(201).json({
message: 'Thing updated successfully!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
}
);
};In this modified version of the function:
First create a new instance of your Thing model with the received _id so as not to cause problems when trying to update that Thing in the database.
If you receive a new file with the request (via multer ), handle the form-data and generate the image URL.
If you do not receive a new file, capture the request body JSON.
Save the updated Thing to the database.
Congratulations! Your app now correctly handles file uploads both when putting new things up for sale, and when modifying existing ones.
JSON.parse() turns a stringified object into a usable JavaScript Object.
To reconstruct the full URL of a saved file, you'll need req.protocol and req.get('host') , joining them with '://' , followed by req.file.filename .
Tell your server to serve static files on a given route using express.static() and path.join() .
In the next chapter, you'll see how to expand the back end's delete function!