6 Ways to Write Files in Node.js (Developer's Guide)

Divine Orji
June 6, 2022

TABLE OF CONTENTS

There are a few different methods for writing files in Node.js — each of them with its own pros and cons. In this article, we're going to discuss in detail the differences between writing files synchronously and asynchronously, how we can write files in Node.js using four different synchronous and asynchronous methods, then we'll see how to handle errors when writing files.

Writing Files: Synchronous vs Asynchronous

A synchronous operation in the programming paradigm usually indicates that each statement is executed one after the other and that the next statement is not executed until the current one has finished running. While in an asynchronous operation, multiple statements can be executed concurrently without interfering with each other's execution.

Node.js provides several methods for each of these operations, and in the next part, we'll go through each one thoroughly to help you pick which to use in your project.

Asynchronous methods

Using fs.writeFile()

If the file already exists, this method replaces the file's content with the new specified data; otherwise, a new file with the specified filename is created. Also, the fs.writeFile method requires three parameters: the file name, or file descriptor, the data to be written to the file, and a callback function to handle the returned value.

If, for example, we want to write "John Doe" to a file named "name.txt," our code would look like this:


const fs = require("fs");

let name = "John Doe";
fs.writeFile("name.txt", name, (err) => {
  if (err) {
    console.log(err);
  }
  console.log("File saved!");
});

By adding an optional string to this method, we can also specify the encoding type for the file:


fs.writeFile("name.txt", "John Doe", "utf8", (err) => {
  console.log("File saved!");
});

Furthermore, for a more advanced file writing technique, we can specify an object in place of the encoding as a string so as to add some other choices such as the file writing mode, a signal to terminate the file creation process, and the encoding type itself:


fs.writeFile("name.txt", "John Doe", {
  encoding: "utf8",
  flag: "w",
  // ...
  
}, (err) => {
  console.log("File saved!");
});

Using fs.write()

fs.write() is a low-level function for writing buffer or string data to a file asynchronously. This method is deemed low-level because it can only write to a file and will not create a file if one does not already exist. As a result, it is commonly used interchangeably with the fs.open() method, which first opens the file (or creates it if it does not exist) so that we can then use the fs.write() method to write data to it:


const fs = require("fs");
let name = "John Doe";

fs.open("name.txt", "a", (err, fd) => {
  if (err) {
    console.log(err);
  } else {
    console.log(fd);
    fs.write(fd, name, (err, bytes) => {
      if (err) {
        console.log(err.message);
      } else {
        console.log(bytes + " bytes written");
      }
    });
  }
});

We can optionally provide the position in a file to begin writing data to the file, as well as the part of the data to write out to the file, using this technique. However, in order to do so, our input data must be a buffer, which can also be readily created using the Node.js Buffer module:


let buffer = new Buffer("John Doe");

fs.open("name.txt", "a", (err, fd) => {
  if (err) {
    console.log(err);
  } else {
    fs.write(fd, buffer, 0, buffer.length, (err, bytes) => {
      if (err) {
        console.log(err);
      } else {
        console.log(bytes + " bytes written");
      }
    });
  }
});

Using fs.createWriteStream()

fs.createWriteStream() will create a writable stream to which we can write data at intervals. This method is particularly useful when we need to write data that is available on other sources, such as a third-party server, and may also be a better option compared to methods like fs.writeFile when it comes to writing large amounts of data.

Below is an example of how we can use this method:


const fs = require("fs");

let writer = fs.createWriteStream("sample.txt");
writer.write("Hello world!");

setTimeout(() => {
  writer.write("Hello again!");
}, 2000);

As seen in the above example, we created a write stream, then wrote the string "Hello world!" to the file, and then, after 2 seconds, we used the setTimeout function to write another string, "Hello again!" to the same file.

The createWriteStream method also includes some events with which we can track the progress of the stream and also detect errors if any should occur:


let writer = fs.createWriteStream("sample.txt");
writer.write("Hello world!");

setTimeout(() => {
  writer.write("Hello world!");
  writer.end();
}, 2000);

writer.on("finish", function () {
  console.log("file downloaded to ", "..");
});

writer.on("error", function (err) {
  console.log(err);
});

Using fs.promises.writeFile()

The writeFile method in the Node.js FS module also has a promise-based syntax that can be used in an async/await block to provide clearer syntax while avoiding callback hell. This method operates in the same manner as the standard writeFile method, except that it is used in an asynchronous function and does not require a callback to process the response:


const writeToFile = async () => {
  let response = await fs.promises.writeFile("sample.txt", "Hello world!");
};

Synchronous Methods

Both fs.writeFile() and fs.write() offer a synchronous alternative for writing content to files in a concurrent blocking way. In other words, the next code execution will wait till the file writing operation is complete before proceeding. These method of writing files are best employed when there is a single action that must be completed before any additional code execution can take place.

Using fs.writeFileSync

This method is the synchronous equivalent of the fs.writeFile () method, and it accepts the same parameters except for the callback function:


fs.writeFileSync("name.txt", "John Doe!");

Using fs.writeSync

We also have fs.writeSync(), which is the blocking counterpart of the fs.write() method and will accept the same parameters as this method, except for the callback function. Here's an example of how we can use the fs.writeSync method in conjunction with the fs.openSync method to write to a file in a specific position:


const fd = fs.openSync("name.txt", "r+");
const text = "Janet Doe";
const position = 0;
const numberOfBytesWritten = fs.writeSync(fd, text, position, "utf8");
console.log(`Bytes written: ${numberOfBytesWritten}`);

Handling Error While Writing a File

Errors are inevitable when writing codes, and this applies to working with the Node.js file system modules too. And as we’ve seen in the previous sections of this article, the callback functions in the asyncronous methods discussed include an error argument with which we can check for errors and handle them appropriately.

However, for the synchronous methods, we can pretty much use a try/catch block to handle errors as well. An example is provided below.


try {
  fs.writeFileSync("sample.txt", "My file content");
} catch (error) {
  console.log(error);
}

In this case, if the file system is unable to write this file, the error message will be logged to the console.

Conclusion

Node.js offers many different methods to write files to disk, so it's important to know the proper solution for your intended use case. Here we introduced a few of the most common methods found in Node, explained their major differences, and provided some examples for each.