Real-World Usage & Examples
As a Full Stack Developer, one of the most common backend tasks you’ll perform is working with files — reading logs, storing data, handling uploads, generating reports, etc. In Node.js, this is handled using the powerful fs (File System) module.
What is the fs Module?
The fs module in Node.js allows you to interact with the file system on your machine. You can:
- Create files
- Read files
- Update files
- Delete files
- Rename files
- Work with directories
It comes as a built-in module, so no installation is required.
Importing fs Module
const fs = require('fs');
1. Reading Files in Node.js
Synchronous Method (Blocking)
const data = fs.readFileSync('sample.txt', 'utf8');
console.log(data);
- Execution stops until file is read
- Not recommended for production servers
Asynchronous Method (Non-Blocking)
fs.readFile('sample.txt', 'utf8', (err, data) => {
if (err) {
console.log(err);
return;
}
console.log(data);
});
- Preferred in real-world applications
- Doesn’t block other requests
2. Writing Files
Create or Overwrite File
fs.writeFile('output.txt', 'Hello from Node.js', (err) => {
if (err) throw err;
console.log('File created successfully');
});
Append Data to File
fs.appendFile('output.txt', '\nNew Line Added', (err) => {
if (err) throw err;
console.log('Data appended');
});
3. Deleting Files
fs.unlink('output.txt', (err) => {
if (err) throw err;
console.log('File deleted');
});
4. Renaming Files
fs.rename('old.txt', 'new.txt', (err) => {
if (err) throw err;
console.log('File renamed');
});
5. Working with Directories
Create Folder
fs.mkdir('myFolder', (err) => {
if (err) throw err;
console.log('Folder created');
});
Read Folder Contents
fs.readdir('./', (err, files) => {
if (err) throw err;
console.log(files);
});
Delete Folder
fs.rmdir('myFolder', (err) => {
if (err) throw err;
console.log('Folder deleted');
});
6. Real-World Example: Logging System
Let’s build a simple logging system like you would in a real application.
const fs = require('fs');
function logMessage(message) {
const log = `${new Date().toISOString()} - ${message}\n`;
fs.appendFile('app.log', log, (err) => {
if (err) throw err;
console.log('Log saved');
});
}
logMessage('User logged in');
logMessage('User added product to cart');
This is exactly how backend systems track activity.
7. Pro Tip (Industry Practice)
Use Promises or async/await instead of callbacks:
const fs = require('fs').promises;
async function readFileExample() {
try {
const data = await fs.readFile('sample.txt', 'utf8');
console.log(data);
} catch (err) {
console.log(err);
}
}
readFileExample();
Key Takeaways
- Always prefer asynchronous methods
- Use fs.promises for modern code
- Handle errors properly (very important in production)
- Avoid blocking operations in servers
Node.js File System (fs Module) – Production-Level Understanding
When you move from beginner to professional backend development, the fs module is no longer just about reading files — it becomes part of:
- Logging systems
- Data pipelines
- File uploads (APIs)
- Report generation
- Caching layers
- Backup systems
If you don’t understand fs deeply, you’ll struggle in real-world Node.js projects.
1. Understanding Blocking vs Non-Blocking (Critical Concept)
Node.js is single-threaded, which means:
👉 If you use blocking operations → your entire server freezes
👉 If you use non-blocking → your server handles thousands of users
Blocking Example (Bad for Production)
const fs = require('fs');
console.log("Start");
const data = fs.readFileSync('bigfile.txt', 'utf8');
console.log(data);
console.log("End");
Problem:
- If file is large → server waits
- No other request is processed
Non-Blocking Example (Production Way)
const fs = require('fs');
console.log("Start");
fs.readFile('bigfile.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
console.log("End");
Output order:
Start
End
(file content later)
👉 This is the core strength of Node.js
2. File Descriptors (Low-Level Control)
Most developers skip this, but in real systems (especially MNC-level work), you must understand it.
fs.open('sample.txt', 'r', (err, fd) => {
if (err) throw err;
console.log("File Descriptor:", fd);
fs.close(fd, () => {
console.log("File closed");
});
});
Why Important?
- Helps in large file processing
- Used in streaming systems
- Required for performance optimization
3. Streams with fs (VERY IMPORTANT IN REAL PROJECTS)
If you try to load a 1GB file using readFile, your server can crash.
👉 Solution: Streams
Reading Large Files Using Stream
const fs = require('fs');
const stream = fs.createReadStream('largefile.txt', 'utf8');
stream.on('data', (chunk) => {
console.log("Chunk received:", chunk.length);
});
stream.on('end', () => {
console.log("File reading completed");
});
Why Streams Matter
- Memory efficient
- Faster
- Used in:
- Video streaming
- File uploads
- APIs
Writing Using Streams
const fs = require('fs');
const writeStream = fs.createWriteStream('output.txt');
writeStream.write("Hello\n");
writeStream.write("This is streaming write\n");
writeStream.end();
4. Copying Files (Real-World Task)
const fs = require('fs');
fs.copyFile('source.txt', 'destination.txt', (err) => {
if (err) throw err;
console.log("File copied successfully");
});
5. Checking File Exists (Common Mistake Area)
❌ Wrong (Deprecated style):
fs.exists('file.txt', (exists) => {
console.log(exists);
});
✅ Correct way:
fs.access('file.txt', fs.constants.F_OK, (err) => {
if (err) {
console.log("File does NOT exist");
} else {
console.log("File exists");
}
});
6. File Stats (Metadata Handling)
fs.stat('sample.txt', (err, stats) => {
if (err) throw err;
console.log(stats);
console.log("Is File:", stats.isFile());
console.log("Size:", stats.size);
});
Use Cases
- File upload validation
- Size limits
- Security checks
7. Watching Files (Real-Time Systems)
fs.watch('sample.txt', (eventType, filename) => {
console.log(`Event: ${eventType}`);
});
Used In
- Live reload systems
- Dev tools
- Monitoring systems
8. Promises API (Modern Industry Standard)
Callbacks are outdated in large-scale apps.
const fs = require('fs').promises;
async function fileOperations() {
try {
await fs.writeFile('demo.txt', 'Hello World');
const data = await fs.readFile('demo.txt', 'utf8');
console.log(data);
} catch (err) {
console.log(err);
}
}
fileOperations();
👉 Clean
👉 Scalable
👉 Production-ready
9. Real Project Example: Simple File-Based Database
In small applications, you might store data in JSON.
const fs = require('fs').promises;
async function saveUser(user) {
let users = [];
try {
const data = await fs.readFile('users.json', 'utf8');
users = JSON.parse(data);
} catch (err) {
users = [];
}
users.push(user);
await fs.writeFile('users.json', JSON.stringify(users, null, 2));
}
saveUser({ name: "Rachit", role: "Developer" });
10. Error Handling (VERY IMPORTANT)
Bad code:
fs.readFile('file.txt', (err, data) => {
console.log(data);
});
Good code:
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error("Error:", err.message);
return;
}
console.log(data);
});
11. Security Best Practices
❌ Dangerous
fs.readFile(req.query.filename);
👉 Vulnerable to:
- Path traversal attacks (
../../etc/passwd)
✅ Safe Approach
const path = require('path');
const safePath = path.join(__dirname, 'files', req.query.filename);
12. Performance Tips from Real Projects
From actual MNC-level experience:
- Never use
readFileSyncin APIs - Use streams for large files
- Cache frequently read files
- Use logging wisely (don’t flood disk)
- Rotate logs (important in production)
13. Mini Project: Log Rotation System
const fs = require('fs');
function logMessage(message) {
const file = 'app.log';
fs.stat(file, (err, stats) => {
if (!err && stats.size > 1024 * 1024) {
fs.rename(file, `app-${Date.now()}.log`, () => {});
}
const log = `${new Date().toISOString()} - ${message}\n`;
fs.appendFile(file, log, () => {});
});
}
logMessage("User login");
Final Notes (Important)
If you are preparing students for real-world jobs:
- fs is not just theory → it’s used in every backend system
- Streams + fs = must-know combo
- Always think:
- Performance
- Security
- Scalability
