1

I am working on a 3 layer architecture backend for node JS and Express.

I have to read a file with JSON data and send using HTTP using REST

My problem is that the error thrown from fs.readFile is not being propogated above to upper layer, despite using throw. However the code works with fs.readFileSync.

dao.js

const fs = require("fs");

class DAO {
    async getAllProducts() {
        // try {
        //  fs.readFileSync("products.json");
        // } catch (error) {
        //  throw new Error("Error Reading File");
        // }
        fs.readFile("./products.json", (err, data) => {
            if (err) {
                throw new Error("Error Reading File");
            } else {
                let d = JSON.parse(data);
                return d;
            }
        });
    }
}

module.exports = new DAO();

service.js

const dao = require("../data/dao");

class Service {
    async getAllProducts() {
        try {
            return await dao.getAllProducts();
        } catch (error) {
            throw error;
        }
    }
}

module.exports = new Service();

product.js

const express = require("express");
const router = express.Router();
const service = require("../service/service");
const Response = require("../models/Response");

router.get("/", async (req, res) => {
    try {
        const data = await service.getAllProducts();
        res.status(200).send(new Response(200, "Success", null, data));
    } catch (error) {
        res.status(500).send(new Response(500, "Failure", error.message, null));
    }
});

module.exports = router;

On hitting http://localhost:3000/api/products and using the fs.readFileSync method, the o/p is as expected

{
    "statusCode": 500,
    "status": "Failure",
    "message": "Error Reading File",
    "data": null
}

but on using fs.readFile the o/p is weird

{
    "statusCode": 200,
    "status": "Success",
    "message": null
}

and the console output is below

                                throw new Error("Error Reading File");
                                ^

Error: Error Reading File
    at ReadFileContext.callback (C:\Users\a\b\c\d\data\dao.js:12:11)
    at FSReqCallback.readFileAfterOpen [as oncomplete] (fs.js:264:13)

my guess is because the readfile is an async fn so it's causing problem but to counter that, I have used async/await everywhere so there should'nt be a problem. Not sure where the error is.

any help is much appreciated

1 Answers1

2

That's because the error is thrown in a callback. The error will not be caught from the catch block in your service (check this article which explains this in detail). Instead of using callbacks and the synchronous read-operation you can use fs's promises though:

async getAllProducts() {
     try {        
        const data = await fs.promises.readFile("./products.json");
        return JSON.parse(data);
     } catch (err) {
        console.log(err); // print the orig error but throw your custom error
        throw new Error("Error Reading File");
     }
}

If readFile rejects, the error will now be caught in your service.

One more thing: Exporting an instance from a module is considered bad practice, see this for more information.

eol
  • 23,236
  • 5
  • 46
  • 64
  • Hi, Thanks for the help. Using the above method gives me a message ""message": "ENOENT: no such file or directory, open 'C:\\Users\\harsh\\CB\\Node JS Hands On Lab\\BE\\products.json'" in the message field in response. I was looking for "Error Reading File" as the message field. I wanted to know the correct way to achieve this using the readFile() method, But thanks for the help. Also SInce I have a single instance of Service and DAO for the entire backend (Singleton Pattern) so I exported instances directly. – Lucifer Morningstar Aug 22 '20 at 16:01
  • You can always catch the error from `readFile` and throw your custom execption. I'll edit the answer. – eol Aug 22 '20 at 16:02
  • Thanks, I'll mark this as accepted. Anyway we can use the default fs.readFile() and achieve the same thing? – Lucifer Morningstar Aug 22 '20 at 16:12
  • There's one way: you could wrap the default `fs.readFile` in your own promise and resolve/reject it from the callback if reading the file succeeds/fails. But since node already offers you the promise-version, it does not make a lot of sense :) – eol Aug 22 '20 at 16:31
  • what if I use the .then and .catch on the service layer,, will that work? – Lucifer Morningstar Aug 22 '20 at 16:36
  • 1
    Only with the described way by wrapping `fs.readFile` in a promise, but `try/await/catch` is essentially doing the same. I encourage you to read this: https://itnext.io/error-handling-with-async-await-in-js-26c3f20bc06a It explains it quite well. – eol Aug 22 '20 at 16:45