How to Build a File Upload Rest API in Node.js and Express?

Frameworks such as Express.js assist users in creating Restful APIs. This article will be a tutorial on creating a Node.js REST API that uploads a file to a static folder on the server using Express and Multer. Also, you will work on getting a list of all the files’ details, such as file name and URL, and downloading a file from the server with the link.

The following steps can be taken to build a file upload Rest API in Node.js using Express.js:

  1. Add express, multer, and cors dependencies
  2. Create a file upload Rest API project (optional)
  3. Create a file upload middleware
  4. Create a file upload controller
  5. Define the file upload route
  6. Run and test the file upload.

Don’t worry if you feel a little lost with REST APIs. We’ve got you covered!

1. Add Express, Multer, and Cors Dependencies

Before moving on with making our REST API, certain requirements must be met. First, you must have some knowledge of programming fundamentals in JavaScript and Node.js but, more importantly, a lot of zeal to become a better programmer and learn something new. So keep reading to learn more!

To create this REST API, you will have to install the following on your system:

express 4.17.1
multer 1.4.2
cors 2.8.5

2. Create a File Upload Rest API Project (Optional)

Node.js is a run-time environment that comes with everything you need to run a JavaScript program. Before content is sent to a web browser, it is used to run scripts on the server to render it. You will learn how to set up basic Node.js server in this guide.

The Node Package Manager, or NPM for short, is a repository and application for creating and exchanging JavaScript code.

Therefore, you must have both, Node.js and npm ,  set up on your system.

Once that is finished, open your system’s command prompt and change the current directory to the project’s root folder.

Then use the npm command below to install the Express, Multer, and CORS modules:

npm install express multer cors

After it successfully runs, the package.json file in your project directory will look something like this:

{
  "name": "node-js-express-upload-download-files",
  "version": "1.0.0",
  "description": "Node.js Express Rest API for Uploading Files",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node js",
    "upload",
    "download",
    "file",
    "multipart",
    "rest api",
    "express"
  ],
  "author": "unknown",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "multer": "^1.4.2"
  }
}

3. Create a File Upload Middleware

The middleware will use Multer to manage multipart/form-data and upload files.

Create a file called upload.js inside the middleware folder of your project directory and fill it with the information listed below:

// import the multer module before configuring it to use the disc storage engine
const util = require("util");
const multer = require("multer");
const maxSize = 2 * 1024 * 1024;
let storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, __basedir + "/resources/static/assets/uploads/");
  },
  filename: (req, file, cb) => {
    console.log(file.originalname);
    cb(null, file.originalname);
  },
});

let uploadFile = multer({
  storage: storage,
  limits: { fileSize: maxSize },
}).single("file");

// create the exported middleware object
let uploadFileMiddleware = util.promisify(uploadFile);
module.exports = uploadFileMiddleware;

The code in the previous block imports the multer module before configuring it to use the disc storage engine. For this, we employ the function multer.diskStorage(), which has two properties:

  1. destination: The folder used to store the uploaded files or the path where the images will be stored. We’ll make it “./uploads.”
  2. filename: chooses the name that will be saved in storage or the file’s name that will be placed in the desired folder. Simply using its original name will suffice.

Lastly, util.promisify() creates the exported middleware object that can later be used with async-await.

Restrict File Size With Multer

With the new multer API, users can limit the size of files by adding limits: { fileSize: maxSize }  to the object passed to multer(). Our file size will be limited to 2 MB. Let’s examine this in practice.

let storage = multer.diskStorage(...);
const maxSize = 2 * 1024 * 1024; // 2 MB
let uploadFile = multer({
  storage: storage,  // adding limits
  limits: { fileSize: maxSize }
}).single("file");

4. Create a File Upload Controller

To create a controller for our REST API, first, navigate to the controller folder and create file.controller.js:

To define the File Upload method, we export upload() function that will do the following:

  1. It will use a middleware function to upload a file.
  2. Next, it will catch Multer errors inside the middleware function.
  3. Finally, return response with message.
const uploadFile = require("../middleware/upload");
const upload = async (req, res) => {
  try {
    await uploadFile(req, res);
    if (req.file == undefined) {
      return res.status(400).send({ message: "Upload a file please!" });
    }    
    res.status(200).send({
      message: "The following file was uploaded successfully: " + req.file.originalname,
    });
  } catch (err) { \\ error handling
    res.status(500).send({
      message: `Unable to upload the file: ${req.file.originalname}. ${err}`,
    });
  }
};

module.exports = upload;

Let’s review the key components of this code. We start by calling the middleware function uploadFile(). Then, send the 400 status code in the response if the HTTP request doesn’t contain any files. Then, the error is also caught, and a 500 status is sent with an error message.

Once that is resolved, we need to write some code to handle instances when users try to upload files larger than the allowed size.

Handle Multer File Size Limit Error

Moreover, the error can be handled by checking the error code (LIMIT_FILE_SIZE) in the catch() block:

const upload = async (req, res) => {  // try to upload
  try {
    await uploadFile(req, res);
    ...
  } catch (err) {    // error handling
    if (err.code == "LIMIT_FILE_SIZE") {
      return res.status(500).send({
        message: "File larger than 2MB cannot be uploaded!",
      });
    }
    res.status(500).send({
      message: `Unable to upload the file: ${req.file.originalname}. ${err}`,
    });
  }
};

5. Define the File Upload Route

Whenever the server receives an HTTP request from a client, we need to determine how the server should respond by setting up the route:

POST /upload: upload()

So we need to create an index.js file inside the routes folder that will contain the following code:

const express = require("express");
const router = express.Router();
const controller = require("../controller/file.controller");
let routes = (app) => {
  router.post("/upload", controller.upload);
  app.use(router);
};
module.exports = routes;

Create the Express App Server

Finally, we can create an Express server in server.js:

// import modulesconst cors = require("cors");
const express = require("express");

// create REST apiconst app = express();
global.__basedir = __dirname;
var corsOptions = {
  origin: "http://localhost:8080"
};
app.use(cors(corsOptions));
const initRoutes = require("./src/routes");
app.use(express.urlencoded({ extended: true }));
initRoutes(app);// port where server will run
let port = 8080;
app.listen(port, () => {
  console.log(`Running at localhost:${port}`);
});

The express and cors modules are first imported by the code above. Express is used to create the REST API, and cors offers Express middleware with several options to enable CORS. The code then creates an Express app to add cors middlewares the app.use() method. Take note of the default setting, which instructs the server to listen on port 8080 for incoming requests.

So, after all this coding, your project directory should contain the following:

  • resources/static/assets/uploads: The folder for storing uploaded files.
  • middleware/upload.js: This initializes the Multer Storage engine and defines the middleware function to save uploaded files in the uploads folder.
  • file.controller.js exports Rest APIs: With the controller, you can POST a file, GET all files’ information, or download a file with a URL.
  • routes/index.js: This defines routes for endpoints that are called from HTTP Client and uses a controller to handle requests.
  • server.js: initializes routes and runs the Express app.

6. Run and Test the File Upload

Running the Server

Before moving on, the uploads folder must first be created with the path resources/static/assets.

The server will then be started by running the following command in the project root directory:

node server.js.

You should receive a notification that the server is starting up and listening on the designated port, in this case, 8080.

Let’s now make an HTTP POST request with a file using Postman on the client-side.

What is Postman?

Postman is perhaps the most popular API testing tool on the market. It is an HTTP client that examines HTTP requests using a graphical user interface, allowing us to get various responses that must then be verified. To help you create better APIs faster, Postman streamlines collaboration and simplifies every stage of the API lifecycle.

Postman has numerous endpoint interactions. Here are some of them:

  • GET: Obtain information 
  • POST: Add information
  • PUT: Replace information
  • PATCH: Update certain information
  • DELETE: Delete information

Tip: For a smoother experience and to quickly get started using the Postman API Platform, download the Postman desktop application. Additionally, you must download the Postman desktop agent if you use the Postman web client. The Postman agent enables API request sending from your browser version of Postman by getting around browsers’ Cross Object Resource Sharing (CORS) restrictions. So another good program to install for convenience is Postman Agent.

Testing With Several Requests

To test this, open Postman, choose the POST option, and then enter http://localhost:8080/upload next to it. Next, select the file you want, such as user.doc, and click “Send” to send the request. You can anticipate a response along these lines:

{
message: "The following file was uploaded successfully: user.doc"
}

After uploading the files, you should check the uploads folder to see if those files are there.

Now, if you try and upload a file larger than the maximum file size, you will receive the following response:

{
message: "File larger than 2MB cannot be uploaded!"
}

Add File Download and Fetch Files API Endpoints (Optional) 

1. Create File Download and Fetch Files Controller

Like we did above for the File Upload method, we define two functions for file information and download:

getListFiles(): This function returns a list of all files, and their URL, in the uploads folder.

download(): This function receives a file name as input parameter. Then, it uses the Express res.download method to transfer the file as an ‘attachment’ to a path (directory + filename).

const getListFiles = (req, res) => {
  const directoryPath = __basedir + "/resources/static/assets/uploads/";
  fs.readdir(directoryPath, function (err, files) {
    if (err) {
      res.status(500).send({
        message: "There was an issue in scanning the files!",
      });
    }
    let fileInfos = [];
    files.forEach((file) => {
      fileInfos.push({
        name: file,
        url: baseUrl + file,
      });
    });
    res.status(200).send(fileInfos);
  });
};

const download = (req, res) => {
  const fileName = req.params.name;  // define uploads folder path
  const directoryPath = __basedir + "/resources/static/assets/uploads/";
  res.download(directoryPath + fileName, fileName, (err) => {
    if (err) {
      res.status(500).send({
        message: "There was an issue in downloading the file. " + err,
      });
    }
  });
};

module.exports = {
  getListFiles,
  download,
};

2. Create the File Download and Fetch Files Routes

We can have two routes and an associated controller for file listing and downloading: 

GET /files/[fileName]: download()
GET /files: getListFiles()

So we can update the index.js file inside routes folder that will contain the following code:

const express = require("express");
const router = express.Router();
const controller = require("../controller/file.controller");
let routes = (app) => {
  router.post("/upload", controller.upload);
  router.get("/files", controller.getListFiles);
  router.get("/files/:name", controller.download);
  app.use(router);
};
module.exports = routes;

3. Test the File Download and Fetch Files API Endpoint

As you did with file upload, you can get all the information about the files you’ve uploaded by sending a GET request to http://localhost:8080/file. Once again, it will be easiest if you use Postman.

You can also download any file from one of the paths provided in response to the request—for example, http://localhost:8080/files/user.doc.

Wrapping Up

Congratulations! In this post, you learned how to build a Node.js Express Rest API that uploads files into a static folder. In this project, you created a middleware that handles a multipart file using Multer. Also, you learned how to restrict file uploads based on a file size limit and properly handle size limit errors. You have also learned how to use Postman to test your API servers.

Was this article helpful?

Please, let us know your opinions by replying on Twitter to become a better programmer.