1

I'm using the sqlite3 package in node and beginning to flush out my REST API. I've decided to create a promise wrapper around the DB calls and in the controllers, use async/await to call those functions and set that return value to a variable. Then, check the variable and set a response object. For the success cases it's working well, found a thing and set the response. Problem is, in SQLITE3 I'm having a hard time checking for errors. I have a basic check for undefined in the DB service which does throw an error if it encounters an error, but that error goes immediately to the CATCH section of the controller and doesn't allow me to wrap it in an API response I'd like to define. These are separate files (controller vs. service). I'm not finding a lot of helpful information on error checking for sqlite3, it's sparse out there. Ideally the service would throw an error which I could then wrap into a standardized response object and send.

------ LOCATION

const getById = async (req, res, next) => {
    response = {};
    try {
        let location = await getLocationById(req.params.id); // <-- IF NO VALUE FOUND, location IS nothing
        if (error) { // <----- THIS IS WHERE I'D LIKE TO CHECK FOR ERRORS.
            response.status = 404;
            response.message = 'Error attempting to get location';
        } else {
            response.status = 200;
            response.message = 'Success';
            response.data = location;
        }
        res.json(response);
    } catch (error) {
        res.json(response);
    }
};

------------ SERVICE

const getLocationById = (id) => {
    return new Promise((resolve, reject) => {
        let sql = 'SELECT * FROM Locations WHERE id = ?;';
        db.get(sql, [id], (error, location) => {
            if (location !== undefined) {
                resolve(location);
            } else {
                reject(new Error('Error attempting to get location by id'));
            }
        });
    });
};
mike varela
  • 467
  • 1
  • 6
  • 20

1 Answers1

2

You just need to wrap the call to getLocationById() in another try/catch. From there you can decide if you want to wrap or raise the error. If you don't have additional code to add that might throw an error you can remove the outer try/catch. More explanation and suggestions in inline comments:

const getLocationById = (id) => {
  return new Promise((resolve, reject) => {
    const sql = "SELECT * FROM Locations WHERE id = ?;";
    db.get(sql, [id], (error, location) => {
      // check for an error from the db here
      if (error) return reject(error);
      // check for an empty result here and throw your own error
      if (!location) {
        // set a property so we can tell that it wasn't a db error just not found
        const myError = new Error("Error attempting to get location by id");
        myError.notFound = true;
        return reject(myError);
      }
      // otherwise resolve the result
      return resolve(location);
    });
  });
};
const getById = async (req, res, next) => {
  // use const since it is not reassigned, only properties are changed
  const response = {};
  try {
    // code here that might throw an exception
    try {
      // use const if you aren't going to reassign `location`
      const location = await getLocationById(req.params.id); 
      
      // we were able to load the location if we get here
      response.status = 200;
      response.message = "Success";
      response.data = location;
    } catch (error) {
      // maybe check to see if we want to wrap or raise the error
      // maybe call console.log(error) to see the contents
      const doWeWantToWrapTheError = somethingToCheckTypeEtc(error);
      if (doWeWantToWrapTheError) {
        if (error.notFound) {
          // no db errors, just not found
          response.status = 404;
        } else {
          // some kind of db error so set to "internal server error"
          response.status = 500;
        }
        response.message = "Error attempting to get location";
      } else {
        // raise the error to the outer try/catch
        throw error;
      }
    }
    // code here that might throw an exception
    return res.json(response);
  } catch (unexpected) {
    // some other error occurred that was not caught above (unlikely)
    // maybe call console.log(unexpected) to see the contents
    response.error = unexpected.message;
    return res.json(response);
  }
};

doublesharp
  • 26,888
  • 6
  • 52
  • 73
  • This makes sense. Its been such a challenge trying to standardize error reporting. The get clauses are fine, the update action is so frustrating. whether the items are updated or not updated i get a 'this' returned value and it's not discrete in return values, updated returns a value of 1, not updated returns a value of 1. hard to decipher – mike varela Sep 30 '20 at 02:37
  • @mikevarela it sounds like `try/catch` will be your friend there too. If you wrap the update you can tell if it worked or not - a bit more advanced but you can use a transaction to roll back any changes if there is an error too. Probably best to post a new question though :) – doublesharp Sep 30 '20 at 05:38
  • Thanks. Been using double try catch blocks and it’s working well now. Seems a little convoluted. But at least it’s working as expected. – mike varela Oct 01 '20 at 04:21
  • @mikevarela you don't have to use nested `try/catch` necessarily if you know the code isn't going to throw an exception. I used it in my answer as an example but check the comments where it says "code here that might throw an exception" - if you don't have any you can just use one try catch. – doublesharp Oct 01 '20 at 19:47
  • 1
    My issue is I’d like to throw errors when things don’t work. Then in the error part of the main controller, pickup that error and put it into a standard response object. That response object is used for success and error outputs. I wanted to add status error codes to the object so the error message alone wasn’t enough. I found that in the await functions an error would force a fail and the message would be sent. But I wanted a status code. So I’m trying to catch the error first, then refire that with more custom information. Now thinking out loud, this is probably a place to use custom error – mike varela Oct 02 '20 at 15:56