47

Suppose that for every response from an API, i need to map the value from the response to an existing json file in my web application and display the value from the json. What are the better approach in this case to read the json file? require or fs.readfile. Note that there might be thousands of request comes in at a same time.

Note that I do not expect there is any changes to the file during runtime.

request(options, function(error, response, body) {
   // compare response identifier value with json file in node
   // if identifier value exist in the json file
   // return the corresponding value in json file instead
});
Stéphane Bruckert
  • 21,706
  • 14
  • 92
  • 130
vincentsty
  • 2,963
  • 7
  • 34
  • 51

7 Answers7

58

I suppose you'll JSON.parse the json file for the comparison, in that case, require is better because it'll parse the file right away and it's sync:

var obj = require('./myjson'); // no need to add the .json extension

If you have thousands of request using that file, require it once outside your request handler and that's it:

var myObj = require('./myjson');
request(options, function(error, response, body) {
   // myObj is accessible here and is a nice JavaScript object
   var value = myObj.someValue;

   // compare response identifier value with json file in node
   // if identifier value exist in the json file
   // return the corresponding value in json file instead
});
Shanoor
  • 13,344
  • 2
  • 29
  • 40
  • 2
    Isn't `require` synchronous? Depending on the size of the JSON file or use case, fs.readFile should be better. – Andrew Theken Jul 13 '17 at 21:26
  • I too agree and even suggest, using piping for larger files. For the purpose of large connections, pigeon-holing (spatial / temporal indexing) of connections also prove to add great efficiency. – Patrick Sturm Oct 27 '17 at 20:30
  • 9
    it's worth noting that `require` will cache the file, so if the content of that json file changes throughout the lifetime of the application, you won't see the updates – Renaud Feb 27 '19 at 16:59
  • 1
    It's worth noting that due `require` caching when you mutate object you mutate it also for all future calls (so essentially it acts as an **singleton**). I had problem that I used same `require("test-payload.json")` in multiple tests cases and altered some properties on my way. In result one test case passed when ran in isolation but failed when ran with all other tests. So be aware of that and use spread operator to make object copy if needed. – nouveu Sep 02 '21 at 06:28
57

There are two versions for fs.readFile, and they are

Asynchronous version

require('fs').readFile('path/test.json', 'utf8', function (err, data) {
    if (err) 
       // error handling

    var obj = JSON.parse(data);
});

Synchronous version

var json = JSON.parse(require('fs').readFileSync('path/test.json', 'utf8'));

To use require to parse json file as below

var json = require('path/test.json');

But, note that

  • require is synchronous and only reads the file once, following calls return the result from cache

  • If your file does not have a .json extension, require will not treat the contents of the file as JSON.

zangw
  • 43,869
  • 19
  • 177
  • 214
  • 6
    I was wondering why my require('file.json') kept changing until I read this - "require is synchronous and only reads the file once, following calls return the result from cache". This can be bypassed by - delete require.cache[require.resolve('file.json')] – jmathewt Jun 20 '17 at 16:51
15

Since no one ever cared to write a benchmark, and I had a feeling that require works faster, I made one myself.

I compared fs.readFile (promisified version) vs require (without cache) vs fs.readFileSync.

You can see benchmark here and results here.

For 1000 iterations, it looks like this:

require: 835.308ms
readFileSync: 666.151ms
readFileAsync: 1178.361ms

So what should you use? The answer is not so simple.

  1. Use require when you need to cache object forever. And better use Object.freeze to avoid mutating it in application.
  2. Use readFileSync in unit tests or on blocking application startup - it is fastest.
  3. Use readFile or promisified version when application is running and you don't wanna block event loop.
Jehy
  • 4,729
  • 1
  • 38
  • 55
3

Use node-fixtures if dealing with JSON fixtures in your tests.

The project will look for a directory named fixtures which must be child of your test directory in order to load all the fixtures (*.js or *.json files):

// test/fixtures/users.json
{
  "dearwish": {
    "name": "David",
    "gender": "male"
  },
  "innaro": {
    "name": "Inna",
    "gender": "female"
  }
}
// test/users.test.js
var fx = require('node-fixtures');
fx.users.dearwish.name; // => "David" 
Stéphane Bruckert
  • 21,706
  • 14
  • 92
  • 130
2

I only want to point out that it seems require keeps the file in memory even when the variables should be deleted. I had following case:

for (const file of fs.readdirSync('dir/contains/jsons')) {
  // this variable should be deleted after each loop
  // but actually not, perhaps because of "require"
  // it leads to "heap out of memory" error
  const json = require('dir/contains/jsons/' + file);
}

for (const file of fs.readdirSync('dir/contains/jsons')) {
  // this one with "readFileSync" works well
  const json = JSON.parse(fs.readFileSync('dir/contains/jsons/' + file));
}

The first loop with require can't read all JSON files because of "heap out of memory" error. The second loop with readFile works.

GoFindTruth
  • 210
  • 2
  • 6
  • 12
1
{
  "country": [    
    "INDIA",
    "USA"
  ],
  "codes": [   
    "IN",
    "US"
  ]
}

// countryInfo.json

const { country, code } = require('./countryInfo.json');

console.log(country[0]); // "INDIA"
console.log(code[0]); // "IN"
mb870977
  • 51
  • 8
Ajay
  • 41
  • 3
1

If your file is empty, require will break. It will throw an error:

SyntaxError ... Unexpected end of JSON input.

With readFileSync/readFile you can deal with this:

let routesJson = JSON.parse(fs.readFileSync('./routes.json', 'UTF8') || '{}');

or:

let routesJson
fs.readFile('./dueNfe_routes.json', 'UTF8', (err, data) => {
    routesJson = JSON.parse(data || '{}');
});
Afsanefda
  • 3,069
  • 6
  • 36
  • 76
tiagolisalves
  • 503
  • 3
  • 9