ypahalajani
ypahalajani

Reputation: 800

Handle parsing empty file in NodeJS, Is it possible?

I start with an empty file cart.json (just touch cart.json)

I have a default state for the file contents in my mind:

const defaultCart = {
  products: [], // Array<{ id: string; quantity: number; }>
  totalPrice: 0
}

Now, when I read the contents for the very first time (remember the file has no content):

const fs = require('fs');

exports.readCartDataFromFile = (cb) => {
    fs.readFile(filePath, (error, fileData) => {
        const cart = {...defaultCart};
        if (!error) {
            const parsedFileData = JSON.parse(fileData); // throws error
            console.log('parsedFileData: ', parsedFileData);
            /* Do something with the parsedFileData */
        }
        cb(cart);
    });
}

I see this error:

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at /Users/someusername/nodejs/main-app-altered/models/Cart.js:18:41
    at FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:73:3)
[nodemon] app crashed - waiting for file changes before starting...

The temporary solution is that I start the file cart.json with the defaultCart state so that I never come across this error.

But, my question is - Is there a way I could have determined (via some util in code) that the file has no contents so while initializing the model in the constructor, I write the defaultCart state into the file using fs.writeFile?

Upvotes: 2

Views: 1436

Answers (2)

Nicholas Carey
Nicholas Carey

Reputation: 74307

Doesn't seem like it should be much more complicated than this:

const fs = require('fs');

const defaultCart = JSON.stringify({
  products: [], // Array<{ id: string; quantity: number; }>
  totalPrice: 0
});

exports.readCartDataFromFile = (cb) => {
  fs.readFile( filePath, 'utf8', (e, s) => {
    let err = e;
    let cart;

    if (!err) {
      try {
        const json = s.trim().length > 0 ? s : defaultCart ;

        cart = JSON.parse(json);

      } catch (error) {
        
        err = error;

      }
    }
    
    cb(err, cart);

  });
}

But if you want to create/initialize the file with default contents, then something like this would work:

const fs = require('fs/promises');

async function ensureInitialized(filePath) {
  const fileInfo = await fs.stat( filePath ).catch(() => undefined);

  let initialized = fileInfo && fileInfo.size > 0;
  if ( !initialized ) {

    // if the file doesn't exist, or has a size of zero,
    // create/overwrite the default JSON to the file
    await fs.writeFile( filePath, defaultCart, {
      encoding: 'utf8',
      mode: 0o644,
      flag: 'w'
    });

    initialized = true;

  }

  return initialized;
}
const defaultCart = JSON.stringify({
  products: [],
  totalPrice: 0
});

Upvotes: 1

apsillers
apsillers

Reputation: 115970

If you want to test for an empty fileData Buffer inside your read callback, you can just do

if(fileData.length == 0) { ... }

Or, if you want to catch any invalid JSON (including empty strings), you could nest your parse into a try:

try {
    const parsedFileData = JSON.parse(fileData);
    // ...
} catch(e) {
    console.err("Invalid JSON inside cart data file: ", e)
}

Upvotes: 3

Related Questions