Working with folders
Project files are easier to manage when sorted into folders: posts in data/posts, uploaded images in uploads. The node:fs/promises module has functions that create folders, list their contents and delete them.
Creating a folder
mkdir() creates a folder. With recursive: true it also creates any missing parent folders, and if the folder already exists it simply does nothing:
import { mkdir, stat } from 'node:fs/promises';
await mkdir('data/posts', { recursive: true });
console.log((await stat('data/posts')).isDirectory()); // trueThe folders data and posts inside it appear next to the program.
Listing files
readdir() returns an array of the names inside a folder:
import { mkdir, readdir, writeFile } from 'node:fs/promises';
await mkdir('data/posts', { recursive: true });
await writeFile('data/posts/hello.md', '# Hello');
await writeFile('data/posts/files.md', '# Files');
const names = await readdir('data/posts');
console.log(names.sort()); // [ 'files.md', 'hello.md' ]The order of names depends on the system, so the list is sorted for output.
Files and folders
With withFileTypes: true you get objects instead of strings, and they know whether they are a file or a folder:
import { mkdir, readdir, writeFile } from 'node:fs/promises';
await mkdir('data/posts', { recursive: true });
await mkdir('data/images', { recursive: true });
await writeFile('data/posts.json', '[]');
const entries = await readdir('data', { withFileTypes: true });
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
console.log(entry.isDirectory() ? 'folder' : 'file ', entry.name);
}It prints "folder images", "folder posts" and "file posts.json".
Only the files you need
path.extname() helps pick files by extension:
import path from 'node:path';
import { mkdir, readdir, writeFile } from 'node:fs/promises';
await mkdir('drafts', { recursive: true });
await writeFile('drafts/a.md', 'A');
await writeFile('drafts/notes.txt', 'B');
const posts = (await readdir('drafts')).filter((name) => path.extname(name) === '.md');
console.log(posts); // [ 'a.md' ]Deleting and moving
| Function | What it does |
|---|---|
mkdir(dir, { recursive: true }) | Creates a folder with all parents |
readdir(dir) | Lists the names |
rename(from, to) | Renames or moves a folder |
rm(dir, { recursive: true, force: true }) | Deletes a folder with everything inside |
Be careful with rm(): deleted items do not go to the recycle bin.
Common mistake
Without recursive: true, mkdir() fails in two cases: when the folder already exists (EEXIST) and when the parent folder is missing (ENOENT). So a server that creates a data folder at startup always needs the option:
// src/server.js
import { mkdir } from 'node:fs/promises';
await mkdir('data', { recursive: true });Then the second and later starts run without errors.