Reading and Writing Files/Directories in NodeJS Using the FS Core Module
Tags: node
Published: 2021-01-06 14:13:40

Yesterday, I [answered my own SUPER-NOOB questions about servers and NodeJS](https://dev.to/annajmcdougall/what-does-it-mean-to-write-run-a-server-answering-my-own-questions-about-nodejs-35kl). Today, I want to have a look at how we can use a core module within NodeJS which allows us to interact with the file system. This core module is called `fs` (file system).

Let's have a look at some of the simplest operations we can achieve using `fs`.

### A note about asynchronous code

If you aren't already familiar with Promises and the concept of asynchronous code, it is probably best to use the 'Sync' versions of all of the following methods. I won't be covering them here, because they're not what I'm learning, but you can check the NodeJS documentation for more information. 

If you *kinda* get the idea of Promises and/or asynchronous callbacks, then you will probably be fine to get through this.

### Asynchronous callbacks in NodeJS

Each of the following methods follows a similar pattern:

1. The method/function itself, e.g.`fs.readFile(`
2. The file or folder it will be working with (aka the path), e.g. `'./file.txt',` 
3. Additional options, e.g. `{flag: "a"},`
4. A callback function e.g. `(err) => console.log(err ? err : "success"))`

Since all of the following methods are asynchronous, the callback function is what will run *after* the method is complete. It usually takes either one or two parameters, and in all cases listed here, the first parameter is the error message if a problem is encountered.

Now that we've covered the basics, let's have a look at how we can make a new directory.

### Setting up a path variable

For the sake of my examples below, I put some nice little code at the top of my file which looks like this:

```javascript
const fs = require("fs");
const path = require("path");
let currentPath = path.dirname(__filename);
```

The first two lines import the core module native to NodeJS which we need, and then the third line accesses the file we're in (a simple index.js file) and then pulls out the path into its directory using `path.dirname` as the method, and the global variable `__filename`.

By creating the `currentPath` variable, I can more easily test and play with the following methods. If you don't want to do this or something similar, you can also manually enter the path into the directory in which you want to work. Personally, I feel this is easier.

Now, it's important to note that we don't always *need* to use the current path/directory in our methods. We could just use `./` or similar, however as I see it, in future we are likely to be needing to work with other paths outside of our source code, so I assume (perhaps wrongly?) that using the full path is a better habit to build. I'd be interested to hear what more experienced developers think about this in the comments!

## Using `fs.mkdir` to Create a Directory

The following command simply creates a new directory called `testFolder` inside our current folder. Using a template literal i.e. \`a string with backticks\` we can insert our `currentPath` into our first argument.

```javascript
fs.mkdir(`${currentPath}/testFolder`, (err) => {
  if (err) throw err;
});
```

## Using `fs.readdir` to Check the Contents of a Directory

You may be familiar with the `ls` command in the Terminal. This is a similar command, however rather than providing us with a CLI read-out of the files, it returns an array of file and folder names. 

```javascript
fs.readdir(currentPath, (err, files) => {
  if (err) throw err;
  console.log(files);
});
```

When I ran this in my test file, this is what I got back:

```javascript
[ 'index.js', 'test.txt', 'testDir2', 'testDir3', 'testsDir3' ]
```

Additionally, there is a way to get access to what *type* of file is in your directory. Here's a neat little function I came up with:

```javascript
fs.readdir(currentPath, { withFileTypes: true }, (err, files) => {
  if (err) throw err;
  files.forEach((entry) => {
    console.log(`${entry.name}, ${entry.isDirectory() ? "directory" : "file"}`);
  });
});
```

This will allow me to see in my console, whether each item is a directory or a file, using another inbuilt method in Node (I am starting to *love* all these in-builts!) called `isDirectory()` which comes back on file listings when the `withFileTypes: true` object is passed in as an optional second argument.

So what do we get back?

```javascript
index.js, file
test.txt, file
testDir2, directory
testDir3, directory
testsDir3, directory
```

 ### Using `readFile` to Look at File Contents

Let's say we want to look inside the `test.txt` file and see what it says. Unfortunately, data from this file is going to come in encoded. Let me show you what I mean:

```javascript
fs.readFile(`${currentPath}/textInfo.txt`, (err,data) => {
if (err) throw err
}
```

Here's what we get back

```
<Buffer 54 68 69 73 20 66 69 6c 65 20 69 73 20 62 79 20 41 6e 6e 61 20 4a 20 4d 63 44 6f 75 67 61 6c 6c 21 21 21>
```

Uhhh... OK. Well, that's not normal, readable text. WHAT DOES IT MEAN?

Luckily, we can specify what format to use to decode/parse this information. In the case of simple text, utf-8, which we see entered here as a second parameter in string format.

```javascript
fs.readFile(`${currentPath}/textInfo.txt`, 'utf8', (err,data) => {
if (err) {
console.error("ERROR: File reading did not work. Error code " + err)
} else {
console.log("SUCCESS! Here is your data: " + data)
})
```

Now what do we get??

```
This file is by Anna J McDougall!!!
```

Whew, that makes a lot more sense.


### Using `writeFile` to Create a New File or Append Text

Now that you're familiar with the pattern of these commands, let's have a look at a simple example where we create or overwrite a text file:

```javascript
const newText = "Here is some new text!"
fs.writeFile(`${currentPath}/textInfo.txt`, content, (err) => {
if (err) throw (err)
})
```

Great! We now have a file called `textInfo.txt` which has the text "Here is some new text!" within it. Let's try to add some MORE text!

```javascript
const newText2 = "\nI'm so glad we're adding more text";
fs.writeFile(`${currentPath}/textInfo.txt`, newText2, (err) => {
  if (err) throw err;
});
```

Good work! ...Wait, that's not right...

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1609859702620/psMcVazwO.png)

Where did our first text go? D'oh! That's right! `fs.writeFile` overwrites existing file contents! So how can we just add some more text onto the end of our original instead? Using the `a` flag.

```javascript
const newText2 = "\nI'm so glad we're adding more text";
fs.writeFile(`${currentPath}/textInfo.txt`, newText2, {flag: "a"}, (err) => {
  if (err) throw err;
});
```
Aha! Well that looks much better:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1609859833433/6661NFCSG.png)

### Using `fs.stat` to Check Your File Details

Last but not least, let's have a little peek in at our file to see what its details/stats are. Here's a fun little method:

```javascript
fs.stat(`${currentPath}/textInfo.txt`, (err, stats) => {
if (err) throw(err)
console.log(stats)
}
```
This brings us back the following information:

```
Stats {
  dev: 647735127,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 44754521297123880,
  size: 0,
  blocks: 0,
  atimeMs: 1609859928899.2424,
  mtimeMs: 1609859928899.2424,
  ctimeMs: 1609859928899.2424,
  birthtimeMs: 1609859583171.8276,
  atime: 2021-01-05T15:18:48.899Z,
  mtime: 2021-01-05T15:18:48.899Z,
  ctime: 2021-01-05T15:18:48.899Z,
  birthtime: 2021-01-05T15:13:03.172Z
}
```

Wonderful! Now we have a whole heap of details about our text file. I'm sure one day we'll be able to understand and use this information somehow!