DEV Community

Victoria Lo
Victoria Lo

Posted on • Originally published at lo-victoria.com on

Build a REST API with Node.js: Upload an Image File

Hello everyone! Welcome back to the 5th part of Let's Build a Node.js REST API Series. We are so close to finishing this API. Let's not waste any more time and begin!

If you are new to this series, please check out the previous articles to follow along:

  1. Designing and Planning the API
  2. Routes and Controllers
  3. Integrating MongoDB Atlas
  4. Finalizing Controllers

In the previous article, we finally have all our controller functions done and working. Our API can GET, POST and DELETE our tea objects that consists of:

Properties Description Type
name the tea name String
image an image url String
description the description String
keywords words associated with the tea String
origin country where the tea is first made String
brew_time time to brew in minutes Number
temperature best temperature in Celsius to drink Number
comments any comments posted about the tea Array of String

However, in the previous article, I set the image property to just "dummy". In this article, we shall work on setting up this correctly.

Step 1: Install multer and import

For images, we don't just supply a text string like "name" but a file, an image file to be exact. And our image property is a String that will be the path of our uploaded image file.

Simply typing "/myImage.png" in our req.body.image will not work because that path does not exist. We need to upload our image with multer, a node.js middleware useful for uploading files.

Install multer by running:

npm install --save multer
Enter fullscreen mode Exit fullscreen mode

Then let's import multer to our controllers/tea.js file:

const multer = require('multer');
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Storage

Still in our controllers/tea.js file, we add the following code to create a storage where our uploaded images will be stored.

We use multer.diskStorage() and include its 2 properties:

  • destination: the path where the images will be stored. We shall set it as './uploads'.
  • filename: determines the name that would be saved in storage. We can just keep it as its original name.

Here's what it should look like:

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './uploads');
      },
    filename: function (req, file, cb) {
        cb(null, file.originalname);
    }
});
Enter fullscreen mode Exit fullscreen mode

Remember to create an 'uploads' folder in your root directory so it actually exists for the image to be stored there.

Step 3: Upload Image function

Below our const storage, we can initialize multer with multer() and pass storage in its storage property. Next, we have a .single() method which ensures that multer will accept only one file and store it as req.file.

The code will be:

const uploadImg = multer({storage: storage}).single('image');
Enter fullscreen mode Exit fullscreen mode

In our newTea function, we have to change our image property to req.file.path instead of req.body.image because we want image to be our image file's path, not a string from req.body.image.

const newTea = new Tea({
     name:req.body.name,
     image: req.file.path,  //update this
     description: req.body.description,
     keywords: req.body.keywords,
     origin: req.body.origin,
     brew_time: req.body.brew_time,
     temperature: req.body.temperature,
})
Enter fullscreen mode Exit fullscreen mode

Now we just have to export uploadImg to use in our routes/tea.js and include it as middleware. So include this function in our module.exports at the bottom, along with the rest.

module.exports = {
    getAllTea,
    uploadImg,  //include the new guy
    newTea,
    deleteAllTea,
    getOneTea,
    newComment,
    deleteOneTea
};
Enter fullscreen mode Exit fullscreen mode

Now head over to our routes/tea.js file, find the POST /tea route and add uploadImg before newTea.

router.post('/tea', teaController.uploadImg /*insert this guy*/ , teaController.newTea);
Enter fullscreen mode Exit fullscreen mode

Let's test it!

Let's try to POST a new tea with POSTman. Make sure the method is set to POST and the url is correct. Supply some values for each property. For image, set it to 'file' instead of text then upload an image.

set to file.PNG

POSTman should return our new tea object data with our image property saved as a path to our image.

POST image to uploads.PNG

If we check in our 'uploads' folder, the image that we uploaded should be there. That means it works! We can upload images to our tea object.

picture in uploads.PNG

What about GET?

It's pointless to be able to POST if you cannot GET the image right?

Let's try to get the image by entering http://localhost:3000/uploads/green.png as the url in POSTman and set the method to GET. You should see this error being returned:

GET fail.PNG

Why is this so?

Our 'uploads' folder cannot be accessed publicly and therefore the server cannot GET our image. To fix this, we have to make our uploads folder a static file.

Go to server.js and add this line of code:

app.use('/uploads', express.static('./uploads')); 
Enter fullscreen mode Exit fullscreen mode

Now let's retry that test on POSTman and it should return the image correctly.

GET tea.PNG

Congratulations!

Our API is now fully working and built! All there's left to do is to add some security and deploy it for use! That will be in our next and final part of the series. Thank you for reading and following this series, I hope it has been helpful. Stay tuned for the final part. In the meantime, please ask questions or concerns in the comments and refer to the resources below. Cheers!


Further Reading

Notes: All images used in this API is from freepik.com

Oldest comments (0)