Exception handling best practices

What are the latest best practices for catching exceptions in the following?

  • In a Template helper

  • In a global helper

  • In an event handler

  • in a Meteor.call

  • in a Meteor.method

  • in a publish

  • in a subscribe

Also, are the best practices around handling the exception once you’ve caught it?

For example, just logging to the console inside of Chrome on a production box is probably not best way to capture exceptions (but is the default).

Should we use something like the toastr package to alert the user something went wrong, write to the console, and then log to a collection or to the file system?

4 Likes

This calls for a blog post :wink:

what! no!! what I meant was, I’d love to read a blog post about your error
handling strategy :slight_smile:

2 Likes

I’m not sure I’m doing things the ‘right’ way… I’d love to here what others are doing or suggest.

Also, does anyone write the exception to a collection?

I’ve come to realize the right way may be whatever works for you. You can always build upon and improve later but if it works then why not make that YOUR right way. Regardless, I’d be interested in an article about your method too!

Should I wrap the Meteor method in a try/catch – then inside the catch throw a Meteor.Error? Then on the Meteor call, instead of waiting for the callback to log the error, should I wrap the call in a try/catch and log in the catch?

rough example:

/client/meteor_call.js

try {
  Meteor.call('do_something', message);  <- async call, will catch get called?
} catch (e) {
  console.log(e);
}

/lib/meteor_methods.js

do_something: function (message) {
 var return_id = '';
  try {
    return_id = collection_name.insert(message);
  } catch (e) {
    throw new Meteor.Error(500, 'exception in do_something', e);
  } finally {
    return return_id;
  }
}

Above, the Meteor.Error thrown in the Meteor method would bubble up to the Meteor call on the client, and since the client is not handling the error in the callback, won’t the catch on the client catch the it?

If I need to do something with in the callback of the Meteor call, then I can do this I guess:

/client/meteor_call.js

try {
  Meteor.call('do_something', message, function (err, return_value) {
    // do stuff with return_value
  }
} catch (e) {
  console.log(e);
}

But should I place another try/catch within the callback, or just leave it out and catch in the outer try/catch?

1 Like

Right, I get that. But why not ask a collective question to get feedback on what I’m doing?

I agree! I’ve asked several questions about best practices myself. I’ve also spent many hours thinking I was not doing things right when in fact it was just fine, just offering my thoughts mate nothing more. As far as your exception work, I really am interested in how you’re going about this. I’ve had issues trapping errors properly.

I thought that meteor.calls last parameter was a callback function… this is what I have been doing.

Meteor.call('User.invite', profile._id, function (err, userId) {
  if (!err) {
    var user = Meteor.users.findOne(userId)
    sAlert.info("Successfully invited " + user.profile.name + "!");
  } else {
    sAlert.error("Failed to invite user")
  }
});

Then I made a subscribeWithOnError for subscription errors.

subscribeWithOnError = function () {
  check(arguments[0], String);
  var subName = arguments[0];
  var subParams = [].slice.call(arguments, 1, arguments.length);
  var params = [subName].concat(subParams);

  params.push({
    onError: function (e) {
      if (e.error === 401) {
        if (Meteor.user ()) {
          sAlert.error("You are not permitted to view this page");
          Router.current().next();
        } else {
          sAlert.warning("You must be logged in to view this page");
          Router.go('Auth.login');
        }
        // ... etc
      } else {
        Router.current().render('error', {
          data: {
            message: "Unknown Error",
            error: e
          }
        });
      }
    }
  });

  Meteor.subscribe.apply(Meteor, params);
}

Hi, you could simply wrap Meteor.call into your own login and every call show error toast or do whatever you want

Sorry @mrzafod I really don’t understand what you mean.

This is the format for a Meteor call (name, [arg1, arg2…], [asyncCallback])

The callback is optional.

I guess at least one part I’m wondering about is,

Does Meteor.call() swallow exceptions thrown from Meteor.methods()?

if you don’t implement the callback, does the exception from the Meteor method bubble up if the Meteor call is wrapped in a try/catch?

/client/meteor_calls.js

try {
  Meteor.call('do_something', message);  <- async call, 
} catch (e) {
  console.log(e); <- will this catch the exception from the Meteor call?
}

The thing is, I don’t want to implement a callback on each Meteor call just to catch and re-throw the exception from the Meteor method.

Just have a try/catch and deal with your exceptions there.

Out of curiosity, why don’t you want a callback for your methods on the client? Meteor calls are always asynchronous on the client side. There are no fibers on the client to help.

From the documentation

On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn’t have fibers, so there is not actually any way it can block on the remote execution of a method.

Well, it all depends.

But my general rule is, if I don’t need to display something to the user or perform a sequential action (with or without an return value) after the Meteor.call – why have a callback?

Otherwise, I just want a Meteor.call(‘do_something’, message).

But even if I do have a callback, why handle every exception in each callback if I have several that run in a row (async or not)?

My point is, I don’t want to handle exceptions in the callback. I want to handle only functionality that needs to happens in sequence in the callback – nothing more.

From the docs:
Also, ‘unblock: when called, allows the next method from this client to begin running.’ What does this mean to you @khamoud ?

There is no notion of sequential on the client. If you do

funciton race() {   
    Meteor.call("foo");
    Meteor.call("bar");
}

There is no guarantee that "foo" will complete before "bar" even if called without callbacks.

Also, since both calls will return undefined, there is no way to catch error/exceptions even if called within a try/catch block.

What I mean (the notion) of sequential is:

Meteor.call('some_call', some_param, function (err, return_value) {
   // inside here, on the client, i'm happy to be sequential!
   Meteor.call('another_thing_in_seq', return_value, function (err, return_value) {
    // do someting seq
  }
});
1 Like

If you don’t care about the order of which the methods are completing (not returning) then you can call them like that. The problem is that they won’t return anything. You won’t have access to the errors or the return so doing a try/catch block on Meteor.call() won’t help. The only way to catch an error is inside the callback.

1 Like

That’s the question I’ve been asking…

So if I don’t catch the exception in the callback, the exception gets lost? It doesn’t bubble up to the try/catch block the Meteor.call is wrapped in?

This means, I’ll have to do a re-throw in the callback to bubble the exception up to the try/catch block, right?

1 Like

Yeah that’s right. The exception will get lost because all meteor calls return undefined regardless of whether or not they succeed.

1 Like