Skip to content

How to Handle File Uploads in Node.js with Formidable

The Formidable NPM package is a widely used library for handling file uploads in Node.js. This guide demonstrates its use through a simple project.

Below is a preview of the application you will build:

http://localhost:3000

Node.js file upload
application

A basic Node.js file upload application built with the Formidable NPM package

Terminal window
mkdir codesweetly-form-001

Afterward, navigate to your project directory using the command line.

Terminal window
cd path/to/codesweetly-form-001

Once you’re in the app’s directory, use NPM to create a package.json file for your project.

Terminal window
npm init -y

Open the package.json file and delete the main field.

Also, change the type field to module so you can use ECMAScript modules in this tutorial.

{
"scripts": {
"start": "node console.js",
"test": "echo "Error: no test specified" && exit 1"
},
"type": "module"
}

Create an ES Module for your project.

Terminal window
touch form-server.js

Next, open your new module and set up the form for file uploads.

form-server.js
import { createServer } from "node:http";
const server = createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.write(
'<div style="margin: 100px auto; font-family: Arial, sans-serif; line-height: 1.5; color: #333; max-width: 600px;">',
);
res.write(
'<form action="upload-file" method="post" enctype="multipart/form-data" style="border: 1px solid #ccc; padding: 30px 50px; border-radius: 5px; background-color: #f9f9f9; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);">',
);
res.write(
'<label style="display: block; margin-bottom: 30px;"><strong style="display: block; margin-bottom: 5px;">Title</strong> <input style="width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 3px;" type="text" name="title" /></label>',
);
res.write(
'<input type="file" name="uploadedFile" style="display: block; margin-bottom: 20px;" />',
);
res.write(
'<button type="submit" style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer;">Upload File</button>',
);
res.write("</form>");
res.end("</div>");
}
});
server.listen(3000, "localhost", () => {
console.log("Server running at http://localhost:3000/");
});

Here’s what the code above does:

  • Imports the createServer API from Node’s http library into the form-server.js ES Module.
  • Creates and sends a form element to the client when the server receives requests to the root path (/).
  • Configures the port and hostname where the server listens for client requests.

Run the following command in your terminal to install the formidable NPM package:

Terminal window
npm install formidable

Use the Formidable Module in Your Node.js Application

Section titled “Use the Formidable Module in Your Node.js Application”

Add the formidable module and use it to parse form data sent from the client to the server.

form-server.js
import { createServer } from "node:http";
import formidable from "formidable";
const server = createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.write(
'<div style="margin: 100px auto; font-family: Arial, sans-serif; line-height: 1.5; color: #333; max-width: 600px;">',
);
res.write(
'<form action="upload-file" method="post" enctype="multipart/form-data" style="border: 1px solid #ccc; padding: 30px 50px; border-radius: 5px; background-color: #f9f9f9; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);">',
);
res.write(
'<label style="display: block; margin-bottom: 30px;"><strong style="display: block; margin-bottom: 5px;">Title</strong><input style="width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 3px;" type="text" name="title" /></label>',
);
res.write(
'<input type="file" name="uploadedFile" style="display: block; margin-bottom: 20px;" />',
);
res.write(
'<button type="submit" style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer;">Upload File</button>',
);
res.write("</form>");
res.end("</div>");
}
if (req.url === "/upload-file" && req.method.toLowerCase() === "post") {
const form = formidable();
form.parse(req, (err, fields, files) => {
if (err) {
res.writeHead(err.httpCode || 400, { "Content-Type": "text/plain" });
return res.end(`${err}`);
}
console.log("Title:", fields.title);
console.log("Uploaded file:", files.uploadedFile);
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Uploaded file successfully!");
});
}
});
server.listen(3000, "localhost", () => {
console.log("Server running at http://localhost:3000/");
});

Here’s what the code above does:

  • Imports the createServer API from Node’s http library to the form-server.js ES Module.
  • Creates and sends a form to the client when the server receives a request to the root path (/).
  • Uses the formidable module to parse the client’s POST request to the /upload-file URL.
  • Configures the port and hostname where the server listens for client requests.

Now that your form is set up and the server is configured, you can run the application.

Terminal window
node --watch form-server.js
  • node: The command for running Node.js scripts or the REPL.
  • --watch: The flag for activating Node’s watch mode.
  • form-server.js: The JavaScript file you want Node to run.

You can also add the command to the "scripts" field of your project’s package.json file:

package.json
{
"scripts": {
"start": "node --watch form-server.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
}

By so doing, you can run your JavaScript program from your terminal like this:

Terminal window
npm run start

The parse() method is an important part of the formidable API. The next section explains how it works.

The parse() method lets you process a client request that contains form data. You can use it with a callback or as a promise.

The callback-based parse() method takes two arguments. Here’s how it looks:

import formidable from "formidable";
const form = formidable();
form.parse(request, callback);
  • request: The client’s request (the http.IncomingMessage object)
  • callback: A function that handles the form data.

The callback function has three parameters. Here’s the syntax:

function (error, fields, files) { ... }
  • error: An object for handling any errors parse() finds while processing the form data.
  • fields: An object containing the form fields’ names and values.
  • files: An object containing details about the uploaded files.

Here’s an example:

form-server.js
import { createServer } from "node:http";
import formidable from "formidable";
const server = createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.write(
'<div style="margin: 100px auto; font-family: Arial, sans-serif; line-height: 1.5; color: #333; max-width: 600px;">',
);
res.write(
'<form action="upload-file" method="post" enctype="multipart/form-data" style="border: 1px solid #ccc; padding: 30px 50px; border-radius: 5px; background-color: #f9f9f9; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);">',
);
res.write(
'<label style="display: block; margin-bottom: 30px;"><strong style="display: block; margin-bottom: 5px;">Title</strong><input style="width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 3px;" type="text" name="title" /></label>',
);
res.write(
'<input type="file" name="uploadedFile" style="display: block; margin-bottom: 20px;" />',
);
res.write(
'<button type="submit" style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer;">Upload File</button>',
);
res.write("</form>");
res.end("</div>");
}
if (req.url === "/upload-file" && req.method.toLowerCase() === "post") {
const form = formidable();
form.parse(req, (err, fields, files) => {
if (err) {
res.writeHead(err.httpCode || 400, { "Content-Type": "text/plain" });
return res.end(`${err}`);
}
console.log("Title:", fields.title);
console.log("Uploaded file:", files.uploadedFile);
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Uploaded file successfully!");
});
}
});
server.listen(3000, "localhost", () => {
console.log("Server running at http://localhost:3000/");
});

The code above uses the callback-based parse() method to process the form’s data.

The promise-based version of the parse() method

Section titled “The promise-based version of the parse() method”

The promise-based parse() method takes just one argument. Here’s how it looks:

import formidable from "formidable";
const form = formidable();
const [fields, files] = await form.parse(request);
  • fields: An object containing the form fields’ names and values.
  • files: An object containing details about the uploaded files.
  • request: The client’s request (the http.IncomingMessage object)

Here’s an example:

form-server.js
import { createServer } from "node:http";
import formidable from "formidable";
const server = createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, { "Content-Type": "text/html" });
res.write(
'<div style="margin: 100px auto; font-family: Arial, sans-serif; line-height: 1.5; color: #333; max-width: 600px;">',
);
res.write(
'<form action="upload-file" method="post" enctype="multipart/form-data" style="border: 1px solid #ccc; padding: 30px 50px; border-radius: 5px; background-color: #f9f9f9; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);">',
);
res.write(
'<label style="display: block; margin-bottom: 30px;"><strong style="display: block; margin-bottom: 5px;">Title</strong><input style="width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 3px;" type="text" name="title" /></label>',
);
res.write(
'<input type="file" name="uploadedFile" style="display: block; margin-bottom: 20px;" />',
);
res.write(
'<button type="submit" style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer;">Upload File</button>',
);
res.write("</form>");
res.end("</div>");
}
if (req.url === "/upload-file" && req.method.toLowerCase() === "post") {
async function processForm() {
try {
const form = formidable();
const [fields, files] = await form.parse(req);
console.log("Title:", fields.title);
console.log("Uploaded file:", files.uploadedFile);
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Uploaded file successfully!");
} catch (err) {
res.writeHead(err.httpCode || 400, { "Content-Type": "text/plain" });
res.end(`${err}`);
console.error(err);
}
}
processForm();
}
});
server.listen(3000, "localhost", () => {
console.log("Server running at http://localhost:3000/");
});

The code above uses the promise-based parse() method to process the form’s data.

By default, formidable saves uploaded files in your computer’s temporary folder. You can change this setting if needed. The following section explains how.

How to Specify Where Formidable Should Save the Uploaded Files

Section titled “How to Specify Where Formidable Should Save the Uploaded Files”

The formidable() method takes an object that lets you specify the directory for saving uploaded files.

Here’s the syntax:

import formidable from "formidable";
const form = formidable({ uploadDir: "./folder/to/save/uploads" });

Here’s an example:

import formidable from "formidable";
const form = formidable({ uploadDir: "./" });

The code above tells Formidable to save uploaded files in the current directory.

Here’s another example:

import formidable from "formidable";
const form = formidable({ uploadDir: "./uploads" });

The code above tells Formidable to save uploaded files in the uploads folder inside the current directory.