Reading from and Writing to the File System in Node.js

Posted by admin at March 20, 2020

Reading from files is done via the core fs module. There are two sets of reading methods: asynchronous (recommended) and synchronous. In most cases, developers should use async methods, such as fs.readFile because this method won’t block the event loop:const fs = require('fs') const path = require('path') fs.readFile(path.join(__dirname, '/data/customers.csv'), {encoding: 'utf-8'}, function (error, data) { if (error) return console.error(error) console.log(data) })

To write to the file, execute the following:const fs = require('fs') fs.writeFile('message.txt', 'Hello World!', function (error) { if (error) return console.error(error) console.log('Writing is done.') })

Full documentation:

path is a core module that is used to work with file and folder paths so that the code works seamlessly on any platform.


On Windows paths are separated using a \, while on POSIX (Unix, macOS) paths are separated by a /. Therefore, a path might be app\server.js on Windows and app/server.js on POSIX (Unix, macOS). This difference between platforms can cause problems when a path is a hard coded string.

The path.join() method is used to create paths that are platform independent. You should use path.join() instead of hard coding the path because path.join() is guaranteed to work across platforms.

Example of path.join() to create a path to app/server.js:const path = require('path') const server = require(path.join('app', 'server.js'))

You can combine path.join with __dirname to use an absolute path instead of a relative one:const path = require('path') const server = require(path.join(__dirname, 'app', 'server.js'))

Skip to main content

Understanding Event Emitters

Event emitters is a core module for Node developers to implement the observer pattern. The observer pattern has the following: an observer, an event and an event emitter.

The flow goes like this:

  1. A class is created with class
  2. A class inherits from the EventEmitter class using extends
  3. An instance of an object is created from the class with new
  4. An observer (a.k.a. event listener) is created with .on(eventName, eventHandler)
  5. An event is emitted with emit() and the event handler in the observer is executed.
node events - Reading from and Writing to the File System in Node.js

Consider this simple observer pattern code which creates a Job class and then instantiates it. Later on, the object created from the class has an observer/event listener attached (job.on('done'...)) and an event is emitted/triggered.

simple.js:const EventEmitter = require('events') class Job extends EventEmitter {} job = new Job() job.on('done', function(timeDone){ console.log('Job was pronounced done at', timeDone) }) job.emit('done', new Date()) job.removeAllListeners() // remove all observers

The result will be:Job was pronounced done at

Multiple Event Triggers

Events can be triggered/emitted multiple times. For example, in knock-knock.js the knock event is emitted multiple times.

knock-knock.js:const EventEmitter = require('events') class Emitter extends EventEmitter {} emitter = new Emitter() emitter.on('knock', function() { console.log('Who\'s there?') }) emitter.on('knock', function() { console.log('Go away!') }) emitter.emit('knock') emitter.emit('knock')

The result will be:Who's there? Go away! Who's there? Go away!

Executing Observer Code Only once

emitter.once(eventName, eventHandler) will execute observer code just once, no matter how many time this particular event was triggered.

knock-knock-once.js:const EventEmitter = require('events') class Emitter extends EventEmitter {} emitter = new Emitter() emitter.once('knock', function() { console.log('Who\'s there?') }) emitter.emit('knock') emitter.emit('knock')

The result will be:Who's there?

Modular Events

The observer pattern is often used to modularize code. A typical usage is to separate the event emitter class definition and the event emission into its own module but allow the observers to be defined in the main program. This allows us to customize the module behavior without changing the module code itself.

In job.js, we use a generic job module that emits a done event after 700ms. However, in weekly.js, we can customize the event handler of the observer to do whatever we want once a done event is triggered.

job.js:const EventEmitter = require('events') class Job extends EventEmitter { constructor(ops) { super(ops) this.on('start', ()=>{ this.process() }) } process() { setTimeout(()=>{ // Emulate the delay of the job - async! this.emit('done', { completedOn: new Date() }) }, 700) } } module.exports = Job

weekly.js:var Job = require('./job.js') var job = new Job() job.on('done', function(details){ console.log('Weekly email job was completed at', details.completedOn) }) job.emit('start')

When you run weekly.js, the custom logic pertaining to the done event will be executed from weekly.js. This way the creators of the job.js module can keep the module flexible. They don’t have to hard code any logic for the done event in the module. Consumers of the module job.js, people who write weekly.js, have the power to customize the behavior for the done event, and not only for that event. Event emitters can have multiple events: in the middle, at the start, in the end, etc. They can be called (emitted or triggered) multiple times and with data (passed as the second argument to emit() as can be seen in job.js). Furthermore, there are methods to list or remove event listeners (observers) or to specify the execution of an event listener (observer) just once (.once() method).


Write a Reply or Comment