• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 11/24/21

Modify Routes to Take Files Into Account

Log in or subscribe for free to enjoy all this course has to offer!

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 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!

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 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.

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  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! 

Example of certificate of achievement
Example of certificate of achievement