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.
Modify the POST Route
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 stringifiedthing
object. Therefore, you need to parse it usingJSON.parse()
to get a usable object.You need to resolve the full URL because your image
req.file.filename
only contains the filename segment. Usereq.protocol
to get the first segment ('http'
, in this case). Add the'://'
, and then usereq.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!
Modify 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 thatThing
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.
Let's Recap!
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
andreq.get('host')
, joining them with'://'
, followed byreq.file.filename
.Tell your server to serve static files on a given route using
express.static()
andpath.join()
.
In the next chapter, you'll see how to expand the back end's delete function!