Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass error object to socket.io callback

I am using callbacks with socket.io

Client code :

socket.emit('someEvent', {data:1}, function(err, result) {
    console.log(err.message); 
});

Server code :

socket.on('someEvent', function(data, callback) {
    callback(new Error('testing error'));
});

With the above code the client side always prints out undefined. If I change the server side code to the following I can see the error message.

socket.on('someEvent', function(data, callback) {
    callback({message:'testing error'});
});

I can pass my own custom objects to the client just fine, just not the error object. Any ideas?

like image 685
Barış Uşaklı Avatar asked Jan 04 '14 01:01

Barış Uşaklı


2 Answers

socket.io data is serialized as JSON, which can only represent plain objects. You will need to serialize any errors into a recognizable plain-object format, or let Socket.IO's standard serialization do its thing (which will result in a plain empty object for Error instances.

like image 74
loganfsmyth Avatar answered Nov 15 '22 19:11

loganfsmyth


I also think it's odd that Socket.IO doesn't seem to provide explicit built in support for passing Error objects in a meaningful way.

If you want to have Error objects seralized correctly in Socket.IO callbacks you can define a method like this to specify how serialization with .toJSON for Error messages should be handled:

if (!('toJSON' in Error.prototype)) {
    Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
      let result = {}
      Object.getOwnPropertyNames(this).forEach((key) => {
        if (key === 'stack') return
        result[key] = this[key];
      }, this)
      return result
    },
    configurable: true,
    writable: true
  })
}

If you are throwing messages from the server you will want to define this on the server, and if throwing errors from a client to the server you will need to define it on the client too.

Note: In this example it strips the 'stack' property when returning errors to the client (to avoid exposing internals to a client), but this may be something worth leaving in, at least for development mode.

Update: This is a lot easier with ES6

Adapted from a blog post I wrote recently on this:

https://medium.com/@iaincollins/error-handling-in-javascript-a6172ccdf9af

I'm happy to say this easier/cleaner with ES6.

If you define your class extending Error like this:

class ValidationError extends Error {
  constructor(message) {
    super(message)
    this.name = 'ValidationError'
    this.message = message
  }

  toJSON() {
    return {
      error: {
        name: this.name,
        message: this.message,
        stacktrace: this.stack
      }
    }
  }
}

Instead of this being the object that gets passed back:

{ name: 'ValidationError' }

It now looks something like this:

{ 
  error: {
    name: 'ValidationError',
    message: 'A validation error',
    stacktrace: '…'
  }
}

You can see what to expect by doing this:

console.log(JSON.stringify(new ValidationError('A validation error'))
like image 1
Iain Collins Avatar answered Nov 15 '22 19:11

Iain Collins