Skip to content

Node.js File System

The fs module provides file system access. Always prefer the async (promise-based) API in servers to avoid blocking the event loop.

import { readFile, readFileSync } from 'fs/promises'; // modern async
// Async (recommended)
const content = await readFile('./data.txt', 'utf-8');
// Sync (only use in startup code — blocks the event loop)
const content = readFileSync('./data.txt', 'utf-8');

Read JSON:

const raw = await readFile('./config.json', 'utf-8');
const config = JSON.parse(raw);
// Or with require() in CommonJS
const config = require('./config.json');
import { writeFile, appendFile } from 'fs/promises';
// Write (creates or overwrites)
await writeFile('./output.txt', 'Hello, world!', 'utf-8');
// Append
await appendFile('./log.txt', `${new Date().toISOString()} - Event\n`);
// Write JSON
await writeFile('./data.json', JSON.stringify(data, null, 2), 'utf-8');
import { access, constants } from 'fs/promises';
async function fileExists(path: string): Promise<boolean> {
try {
await access(path, constants.F_OK);
return true;
} catch {
return false;
}
}
import { stat } from 'fs/promises';
const info = await stat('./myfile.txt');
console.log(info.size); // bytes
console.log(info.isFile()); // true
console.log(info.isDirectory()); // false
console.log(info.mtime); // last modified date
import { mkdir, readdir, rm } from 'fs/promises';
// Create directory (recursive creates parent dirs too)
await mkdir('./logs/2025', { recursive: true });
// List directory contents
const files = await readdir('./src');
const withStats = await readdir('./src', { withFileTypes: true });
withStats.filter(e => e.isFile()).map(e => e.name);
// Delete directory recursively
await rm('./temp', { recursive: true, force: true });
import { copyFile, rename } from 'fs/promises';
// Copy
await copyFile('./source.txt', './destination.txt');
// Move / rename
await rename('./old-name.txt', './new-name.txt');

Always use the path module — never concatenate paths with strings:

import path from 'path';
// Join path segments (handles OS separators)
const filePath = path.join(__dirname, 'data', 'users.json');
// Resolve to absolute path
const absPath = path.resolve('./relative/path');
// Get parts
path.basename('/usr/local/bin/node'); // 'node'
path.dirname('/usr/local/bin/node'); // '/usr/local/bin'
path.extname('config.json'); // '.json'
// __dirname equivalent in ESM
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
import { watch } from 'fs';
const watcher = watch('./src', { recursive: true }, (event, filename) => {
console.log(`${event}: ${filename}`);
});
// Stop watching
watcher.close();
import { createReadStream } from 'fs';
import { createInterface } from 'readline';
async function processLines(filePath: string) {
const stream = createReadStream(filePath);
const rl = createInterface({ input: stream, crlfDelay: Infinity });
for await (const line of rl) {
// process each line without loading entire file into memory
console.log(line);
}
}
import { readFile } from 'fs/promises';
try {
const content = await readFile('./missing.txt', 'utf-8');
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
console.log('File not found');
} else {
throw err;
}
}
CodeMeaning
ENOENTNo such file or directory
EACCESPermission denied
EEXISTFile already exists
EISDIRIs a directory (expected a file)
ENOTDIRNot a directory (expected a directory)